Здесь авторы заявляют о
следующем:
Авторы книги очень
признательны корреспондентам рубрики «Человек и компьютер» журнала «Наука и
жизнь», письма которых послужили основой некоторых советов книги. Имена многих
из них упоминаются в книге. Многих, но не всех. У тех, чьи имена не попали в
книгу, мы просим извинения.
Авторы, а также рецензент и редактор не имеют единого мнения по поводу обоснованности пп. 1, 5, 6 и 8 вышеприведенного перечня. Поэтому нам будет особенно ценно узнать мнения читателей о книге, которые просим направлять по адресу: 113114, Москва, М-114, Шлюзовая наб., 10, Энергоатомиздат.
Ознакомившись (даже бегло) с
книгой, читатель сразу поймет, что ей не хватает вложенной магнитной дискеты с
записанными текстами программ, как того требует современный уровень
издательского дела.
Авторы надеются,
что читатель захочет опробовать программы книги, модернизировать их, исправить
возможные ошибки, приспособить для своих нужд. Читатель, наверно, попытается
решить задания книги. Но для этого нужно будет сначала перенести тексты
программ в компьютер. Загружать же программы в память машины намного быстрее,
удобнее и безошибочней не из книги (HАRDСОРY), а с магнитного слоя
дискеты (SOFTCOPY).
Отдел технических средств обучения Московского энергетического института (105835, ГСП, Москва, Е-250, Красноказарменная, 14, Центр новых информационных технологий) с согласия авторов и на хозрасчетных началах готов передать дискеты с программами или переписать на дискеты заказчиков (при личном контакте или через почту) программы книги для различных ПК.
Авторы
Глава 0 Стиль написания программ
Совет 00 Великолепная семерка программирования
Совет 01 Как структурируют программы
Совет 02 Как выделяют структуру программы
Совет 03 Один вход, один выход ─ первая заповедь структурного
программирования
Совет 04 Досрочное прерывание цикла
Совет 05 Еще раз о досрочном прерывании цикла
Совет 06 Чему равно значение параметра цикла после выхода из него
Совет 07 Досрочное прерывание программы
Совет 08 Как обнуляют сумматоры
Совет 09 Похожие программы на непохожих языках
Совет 0А Нужны ли новые структурные управляющие конструкции
Совет 0С Неструктурированная структурированная программа
Совет 0D Полная альтернатива без метки
Совет 0F Можно ли сыграть на скрипке с одной струной
Совет 10 Как компьютер откашливается
Совет 11 Двойной ввод значения переменной
Совет 12 Две манеры ввода списков
Совет 13 Диапазон изменения значений элементов списка
Совет 14 Числа одинакового формата
Совет 15 Возможность исправлений во вводимом списке
Совет 16 Запрос бита без прерывания
Совет 17 Естественный ответ на битовый запрос
Совет 1В Блокировка клавиш клавиатуры
Совет 1C Ввод тайной информации
Совет 1D Команды управления магнитофоном
Совет 33 Избегай индексных переменных
Совет 34 Возведение в целую степень
Совет 36 Перестановка мест сомножителей меняет произведение
Совет 37 Знаки логических выражений
Совет 38 Параллельная работа человека и компьютера
Совет 39 Параллельная работа компьютера и принтера
Совет 3A Как ускорить обмен информацией между компьютером и
дисководом
Совет 3B Где размещают подпрограммы
Совет 3C Кесарю – кесарево, машине – машиново
Совет 3E Экономить можно и электроэнергию
Совет 3F Кардинальный путь оптимизации
Глава 4. Психология программирования
Совет 40 Искусство – это чувство меры
Совет 41 Еще одно преимущество простого алгоритма
Совет 42 Что делать, если приходится отчитываться за загрузку
персонального компьютера
Совет 43 Семь раз отмерь – один раз отрежь
Совет 44 Не злоупотребляй принципом умолчания
Совет 45 Программа без тайны – не программа
Совет 46 Испытание версий языка
Совет 47 Два дисплея – два окна
Совет 4В Придерживайся своего стиля
Совет 4С Как можно искать задачи для ЭВМ
Совет 4D Численный эксперимент – хлеб компьютера
Глава 5 "Что в имени тебе моем..."
Совет 50 Имя ─ это повесть из одного названия
Совет 51 Имена меняют облик программы
Совет 54 Имя переменной на Бейсике
Совет 55 Имя переменной и служебное слово
Совет 56 Ловушки целочисленных переменных
Совет 57 Целочисленная переменная и массив
Совет 58 Вещественная целочисленная переменная
Совет 59 Переменная двойной точности
Совет 5A Локальные и глобальные переменные
Совет 5B Имя пользователя программы
Совет 5C Главный файл на диске
Совет 5D Два пути рождения файла на диске
Совет 5E Как переменная окликает другую
Совет 5F Вспомни об имени программы и в конце работы с ней
Совет 60 Подготовка листинга программы к публикации
Совет 62 Выделение ключевых слов
Совет 63 Что еще можно выделить в программе
Совет 64 Комментарии на другом языке
Совет 65 Форма и содержание программы
Совет 67 Может ли программа сама себя распечатать
Совет 68 Комментарии в блоках данных
Совет 69 Разные шрифты в листинге программы
Совет 6A Протокол прогонки программы с использованием разных
шрифтов
Совет 6C Короткие программы в одну строку
Совет 6D Порции информации на дисплее
Совет 6F 24 строки и 32 совета
Совет 70 Вторая дырка в конверте диска
Совет 71 Рисуем по стеклу дисплея
Совет 72 Что заправляют в принтер
Совет 73 Колпачки на клавишах клавиатуры
Совет 74 Тайные особенности аппаратных и программных средств
Совет 75 Не пользуйся советом 74
Совет 76 Нужна ли ЭВМ педаль!?
Совет 77 Как включают периферию
Совет 78 Береженого бог бережет
Совет 79 Полуфабрикаты на диске
Совет 7А BASIC-строки с одним комментарием
Совет 7В Персональный компьютер и компьютер с разделением времени
работы процессора
Совет 7С Имитация часов на компьютере
Совет 7D Два способа тасовки карт
Совет 7Е Как принимают решения с помощью ЭВМ
Совет 7F Timeo Danaos et dona ferentes
В
настоящее время по ряду объективных причин сложилась ситуация, когда первым
языком программирования, с которым сталкивается начинающий, является либо
BASIC, либо язык команд микрокалькулятора. BASIC, если говорить о его самых
распространенных версиях, имеет слаборазвитую структуру данных, а также метки и
условные переходы как средство реализации сложных алгоритмов. Все это не
позволяет рекомендовать BASIC в качестве учебного средства при изучении
программирования. Но! Какой первый язык человеческого общения учит ребенок?
Конечно, не самый правильный и самый простой, а тот, на котором говорят
окружающие. Так получилось и с BASIC'ом, и с языком микрокалькулятора. Несмотря
на их существенные недостатки (а многие не без серьезных оснований считают
BASIC "вредным" языком, прививающим начинающему программисту плохой
стиль) BASIC и язык микрокалькулятора ... см. начало абзаца. Объективными же
причинами такого положения являются дешевизна и доступность калькуляторов и то,
что все персональные компьютеры в качестве основы матобеспечения имеют
транслятор BASICа. Часто этот транслятор хранится в ПЗУ, т.е. "вшит"
в ПК.
В данной главе будет рассказано, как, работая с любым языком,
можно приучить себя к красивому стилю программирования. Мы, кроме того,
рекомендует читателю после знакомства с азами программирования обратиться к
книгам [8-10, 18, 19, 22, 24], где более подробно рассказано о стиле
программирования.
При написании программ используй семь и не более управляющих конструкций,
изменяющих естественный ход выполнения операторов:
1) цикл "пока";
2) альтернативу;
3) множественное ветвление;
4) цикл "до";
5) цикл с параметром;
6) вызов процедуры (подпрограммы);
7) переход к метке.
Под естественным ходом выполнения операторов программы понимается
процесс, подобный чтению книги: слева направо и сверху вниз.
Поясним перечисленные управляющие конструкции (их еще называют
структурными) примерами.
Рис.I
Рис.0.1. BASIC-программа поиска корня алгебраического уравнения
методом секущих: цикл "пока"
На рис. 0.1 помещены BASIC-программы (вернее, варианты реализации)
поиска корня алгебраического уравнения вблизи двух заданных точек (Х1 и Х2 ─ см.
строку 20) методом секущих. Сущность метода [4, 29] в том, что через точки на
графике функции Y(Х1) и Y(Х2) проводится секущая, пересечение которой с осью Х
дает первое приближение к корню уравнения (см. строку 40). Эта новая точка на
оси Х становится второй опорной, а вторая ─ первой (см. строку 50); сам
же процесс приближения к корню продолжается до тех пор, пока новую секущую
нужно будет проводить через точки, ординаты которых отстоят друг от друга менее
чем на величину заданной точности (Е ─ см. строку 20).
Операторы вычисления нового значения приближения к корню (см.
строку 40) и подготовки к последующему приближению (см. строку 50) составляют
тело цикла "пока". Этот участок программы (функциональный блок)
выполняется, пока верно логическое выражение, записанное в заголовке цикла
(строка 30).
Анализируемое уравнение перед запуском программы должно быть
записано на строке 10 функцией пользователя.
Рис.II
Рис.0.2. BASIC-программа обработки календарной даты ХХ века:
неполная альтернатива и множественное ветвление
Программа определения номера дня и дня недели календарной даты
(рис. 0.2) иллюстрирует две другие управляющие конструкции: альтернативу
(строки 60-80) и множественное ветвление (строки 120-150). Структурный вариант
программы помещен на рис. 7.6. Мы не будем разъяснять сущность алгоритма (он
довольно прост), а
отошлем любопытных к [45].
В заголовке альтернативы (см. строку 60) записано логическое
выражение. В зависимости от его правильности или неправильности выполняется
правое или левое (строки 70 и 80) плечо альтернативы. В нашем случае правое
плечо альтернативы пусто.
В заголовке множественного ветвления записано алгебраическое
выражение, дающее (программисты говорят "возвращающее") целочисленный
результат: 1, 2, 3 и т.д., в зависимости от которого выполняются первый (строки
130 и 140), второй (строки 150 и 160) и т.д. (слева направо) функциональные
блоки, печатающие названия дней недели и передающие управление программой в ее
конец.
Неполная альтернатива (с одним пустым плечом) является частным
случаем полной, которая в свою очередь является частным случаем множественного
ветвления. С другой стороны, множественное ветвление можно заменить рядом
альтернатив.
В цикле "пока" (см. рис. 0.1) сначала проверяется
условие, записанное в заголовке, а потом выполняется тело цикла. Но часто
бывает необходимо сделать наоборот: сначала выполнить функциональный блок,
составляющий тело цикла, а потом уж решить, нужно ли его выполнять повторно.
Рис.0.3. BASIС
– прогамма
поиска корня алгебаического уавнения
методом Ньютона:цикл "до"
Такую последовательность действий реализует четвертая структурная
управляющая конструкция ─ цикл
"до". У нас он иллюстрируется программой (рис. 0.3) поиска корня
алгебраического уравнения методом Ньютона. Он является частным случаем метода
секущих (см. рис. 0.1), когда две исходные точки на оси Х сливаются в одну. В
этом случае секущая превращается в касательную, а ее положение определяется с
помощью первой производной (см. строку 20 на рис. 0.3). Программа на рис. 0.3
написана на структурной версии BASIC'а, где нет нужды прибегать к меткам. На
неструктурных версиях цикл "до" реализуется условным переходом вверх.
В программе, помещенной на рис. 0.3, это будет выглядеть так:
60 IF ABS (X1 ─ X2) >= E GOTO 50
Строку 40 при этом нужно будет убрать.
Рис.0.4.BASIC
– погаммасотиовки
вводимого с клавиатуы числового массива: цикл с пааметом и цикл
"пока"
Если заранее известно, сколько раз нужно выполнить тело цикла, то
вместо цикла "пока" или цикла "до" лучше использовать пятую
конструкцию ─ цикл с
параметром. В заголовке этого цикла указываются переменная-параметр, ее
начальное и конечное значения, а также шаг после каждого прохождения тела
цикла. Эта структурная конструкция иллюстрируется программой (рис. 0.4)
сортировки в порядке убывания одномерного числового массива. Его размер ─ не более
1000 элементов (см. первый оператор строки 10).
На строке 20 записан заголовок цикла с параметром I, который
изменяется от 1 до N с шагом 1 (по умолчанию). Конец цикла отмечен словом NEXT
(следующий ─ см.
строку 60). Тело цикла (сроки 30 ─ 50) ─ это
функциональный блок, который в свою очередь включает в себя цикл
"пока" (в этом случае говорят, что один цикл вложен в другой).
Алгоритм сортировки построен так, что новый элемент массива сначала
пристраивается в его хвост (строка 30), а затем передвигается в голову до тех
пор, пока (цикл "пока"!) слева от него не окажется элемент, имеющий
большее или равное значение, или передвигаемый элемент не станет самым первым.
После этого вводится следующий (NEXT) элемент, с которым проделываются те же операции.
После обработки последнего элемента отсортированный массив выводится на печать
(см. строку 70).
Следует помнить о трех "нельзя" при обращении к циклу с
параметром:
1) нельзя изменять параметр цикла в его теле;
2) нельзя досрочно выходить из цикла с параметром;
3) нельзя использовать значение параметра цикла после выхода из
него.
В книге будет рассказано подробно об этих запретах.
Рис.0.5.BASIC
– погамма
"Частотный словаь": последовательность выполнения опеатоов
На рис. 0.5 представлена программа составления частотного словаря,
введенного через клавиатуру ЭВМ текста. В программе задействованы все семь
структурных управляющих конструкций. Пять первых из них описаны ранее, а о двух
последних (вызов процедуры и переход к метке) будет рассказано по ходу разбора
программы.
Гвоздь программы ─ строка 20, в которой ищутся
знаки препинания и пробелы, разделяющие слова текста. Коды этих символов
меньше, чем код заглавной латинской буквы А (о кодах символов читай в совете
52). Дефис (-) и апостроф (заменитель твердого знака: ’) приравниваются к
буквам. Составленный программой частотный словарь выводится на печать либо по
алфавиту (по тому, конечно, какой "вшит" в ЭВМ), либо в порядке
убывания значения частоты встречи слова в тексте (см. меню на строке 90).
Если программирование можно уподобить игре на семиструнной гитаре,
то вот те струны, рождающие мелодию ─ решение на ЭВМ поставленной
задачи:
1) цикл "пока": сортировка формируемого словаря в
порядке убывания частоты встречи слов в тексте (строка 70 без первого
оператора). Проводить такую операцию полезно даже в том случае, если
составленный словарь придется выводить на печать по алфавиту. Сортировка в
порядке убывания ускоряет работу программы: если какое-то слово часто используется
в первой половине текста, то вполне обоснованно ожидать частой встречи этого
слова и во второй половине. Дело в том, что выделенное слово сравнивается со
словами словаря, начиная с его начала, где будут расположены наиболее часто
встречающиеся слова;
2) альтернатива: неполная ─ игнорирование промежутков
между словами с двумя и более знаками препинания или пробелами (строки 30-70);
полная ─
уточнение, встречалось ли ранее в частотном словаре только что выделенное слово
(строки 60-70). Если встречалось (строка 70), то частота этого слова
увеличивается на единицу, а соответствующие элементы массивов по возможности
передвигаются в начало словаря (см. п. 1). Если слово не встречалось (конец
строки 60), то словарь расширяется на одну позицию, куда и заносится вновь
выделенное из текста слово;
3) множественное ветвление после ответа на запрос по меню (строки
110-130). Здесь, правда, можно было обойтись простой неполной альтернативой с
пустым правым плечом, но режимов вывода словаря может быть больше двух: вывод
на принтер, сброс на диск, на перфоленту и т.д.
Непронумерованный совет. Из нескольких равноценных программных
приемов выбирай тот, который при прочих равных параметрах обеспечивает
возможность модернизации и расширения программы без особых затрат времени;
4) цикл "до": поиск в очередной строке анализируемого
текста знаков-разделителей слов (строка 20); перебор слов в частотном словаре
для их сравнения с только что выделенным из текста (строка 50); перебор слов
очередной строки текста (программные строки 20-80); опрос клавиатуры при
ожидании ответа человека на запрос по меню (строка 100);
5) цикл с параметром: ввод исходного анализируемого текста (строка
11 без последнего оператора); перебор строк при анализе (строки 12-90);
распечатка частотного словаря (строка 140 без последнего оператора). Строки 120
и 130 имеют все признаки цикла с параметром, но тем не менее это не цикл с
параметром (см. пункт 7);
6) вызов процедуры (подпрограммы) перестановки соседних слов
частотного словаря. Данная структурная конструкция, как правило, применяется в
том случае, когда какой-то функциональный блок встречается в программе более
одного раза. В этом отношении подпрограмма подобна припеву песни, который
исполняется неоднократно, хотя в тексте записан всего раз. В нашей песне,
просим прощения, программе, процедура перестановки слов словаря нужна в случае
его сортировки и по частоте (строка 70), и по алфавиту (строка 120). Вот откуда
появилась подпрограмма на строке 150.
Непронумерованный совет. Не забудь в конце программы перед началом
подпрограмм поставить оператор, прерывающий ее выполнение. В противном случае
машина, выполнив основную программу, "залезет" в подпрограмму
"не спросясь" ее, т.е. без использования оператора вызова
подпрограммы, что приведет к аварийной ситуации;
7) переход к метке. В чистом виде это делается только при
досрочном прерывании цикла с параметром и возвращении к его началу при
сортировке словаря по алфавиту (см. строку 120). В остальных случаях переходы к
меткам реализуют какую-либо одну из стандартных управляющих конструкций (см.
п.п. 2, 4 и 6). На строках 120 и 130 переход к метке "поломал" цикл с
параметром (см. п. 5).
Упоминание о метке неслучайно оказалось в последнем пункте нашего
списка. Ценность этого приема (а он широко используется на языках BASIC,
Fortran, Фокал и др.) очень низкая. Считается [8,18], что квалификация
программиста обратно пропорциональна числу меток в его программах, написанных
на структурных языках.
Структурные диаграммы допускают переход к метке в пределах
функционального блока, ограниченного прямоугольными рамками без внутренних
перегородок (см. строки 120 и 130).
Многие полагают, что структурированная программа ─ это
программа без меток. Но это не так. Структурированная программа -это программа,
в которой либо переходы к меткам реализуют стандартные управляющие конструкции,
либо эти конструкции реализованы специальными программными средствами. Можно
написать структурированную программу на неструктурном Fortranе (см. рис.0.22,
а) и неструктурированную программу на структурном Паскале (см. рис. 5.15).
Исключение меток из программы ─ операция отнюдь не
эстетическая, ставящая своей целью создания мнения об авторе программы, как об
опытном программисте. В этом можно убедиться, прочитав советы 03, 04, 05, 07 и
др.
Если же мы ранее упомянули о гитаре как о подобии инструмента
программирования, то эту аналогию можно продолжить, упомянув странное
совпадение: у нас в стране программирование без меток получало распространение
в такое же примерно время, когда шестиструнные гитары начали вытеснять
семиструнные.
Отметим еще несколько особенностей программы "Частотный
словарь", помещенной на рис. 0.5.
1. В ней использовано три способа ввода информации в BASIC-машину
через клавиатуру: ввод числа или цепочки символов, заканчивающейся после нажатия
клавиши "Ввод" (см. начало строки 10); ввод цепочки символов,
содержащей и запятые, которые обычно принимаются за знаки-разделители констант
(второй оператор строки 11); ввод байта без прерывания (см. начало строки 100).
Последний способ незаменим в динамических играх. Но и здесь при ответе на
запрос по меню он очень удобен: во-первых, блокируются все клавиши, кроме
нужных в данных момент (1 или 2), а, во-вторых, нет нужды нажимать в конце
клавишу "Ввод". Об этом подробнее сказано в советах 16 и 17.
2. В программе "Частотный словарь" применяются два
принципиально разных способа сортировки массивов: сортировка в процессе
заполнения массива (строка 70) и сортировка ранее заполненного массива (строки
120-130). В том и другом случае выбраны не самые быстрые, но самые короткие
алгоритмы [27]. Есть еще и третий способ сортировки массива, основанный на
переносе его членов в заранее зарезервированный новый массив (см. рис.2.7).
3. Короткие структурные конструкции, содержащие в своем теле один
или два оператора (см. строки 11 и 140), рамками структурной диаграммы не
выделены.
Программа "Частотный словарь имеет ряд допущений. Вот два из
них:
1) слово текста, введенное с переносом на следующую строку,
машиной принимается за два самостоятельных;
2) одно и то же слово, использованное в разных грамматических
формах, машиной также принимается за отдельные слова.
Непронумерованный совет. Давая описание программы, не забудь
отметить допущения, принятые в решении поставленной задачи. Во-первых, это
убережет тебя от критики пользователя, а самого пользователя ─ от
ошибок. Во-вторых, это подтолкнет кого-то к творчеству.
Задание читателю. Доработай программу "Частотный
словарь", исключив в ней принятые допущения, имея в виду, что научить
машину разбираться в русских склонениях, спряжениях, родах и прочем не менее
сложно, чем научить ее расчетам траектории полета станции "Фобос" на
Марс.
Рис 06 Стандартные управляющие конструкции алгоритмов:
1) функциональный блок
2) цикл "пока"
3) альтернатива
4) цикл "до"
5) цикл с параметом
6) множественное ветвление
Рис 07 Диаграммы Ненси – Шнейдермана
1) альтернатива
2) цикл "пока"
3) цикл "до"
На рис. 0.6 представлены элементы блок-схемы и структурной
диаграммы. При разработке структурных диаграмм авторы книги взяли за основу
диаграммы Несси-Шнейдермана (рис. 0.7) [16, 46], изменив их так, чтобы в них
можно было помещать не только описания алгоритмов, но и сами программы [37].
Структурируя алгоритмы и программы, пользуйся двумя методами:
дублированием и использованием признака.
Этот совет можно расписать на целую книгу, что, кстати, уже и
сделано [8, 10, 18, 22]. Вопрос о том, можно ли алгоритм любой сложности
реализовать через комбинации шести структурных управляющих конструкций (см.
совет 00), решается так называемой основной структурной теоремой [18]. Но об
этом будет рассказано несколько позже ─ в советах 0Е и 0F. Сейчас
же примем на веру правильность этой теоремы и рассмотрим способы
структурирования, используя примеры.
Рис.0.8. схема структурной программы "Угадай число":
а) неструктурированная BASIC – программа
б) структурированная BASIC – программа
в) Pascal
– программа
Программы на рис. 0.8 реализуют на ЭВМ популярную игру
"Угадай число": машина загадывает число в диапазоне от 1 до 1000 (см.
строку 30), которое человек должен отгадать за минимальное число попыток. Этой
игрой часто иллюстрируют принцип бинарного (двоичного) поиска. Придерживаясь
оптимальной стратегии, человек должен делить интервал неопределенности пополам,
а число, оказавшееся в середине, сообщать машине в качестве очередной догадки
(см. строку 40). По ответам ЭВМ "Перелет" или "Недолет"
интервал неопределенности уменьшается вдвое. Программа на рис.0.8, а реализует
"естественную" последовательность действий в игре: если догадка
человека больше (см. строку 50) или меньше (см. строку 40) задуманного числа,
то (THEN) машина сразу сообщает об этом и запрашивает новую догадку. Программа
на рис. 0.8, а ─
неструктурированная. Структурировать ее можно, продублировав некоторые действия
и тем самым преобразовав в программу, помещенную на рис. 0.8, б. Здесь
операторы безусловного перехода на строках 50 и 60 убраны, но появилась строка
65, на первый взгляд, лишняя: ведь если В не больше А и В не меньше А, то
незачем дополнительно проверять равенство этих чисел. Но эта дублирующая
операция структурировала программу. Теперь (рис. 0.8, б) алгоритм игры
реализуется через цикл "до", тело которого содержит две
последовательные неполные альтернативы. Такую программу несложно перевести на
структурный язык Pascal (рис. 0.8, в).
Рис.III
Второй метод структурирования (использование признака)
иллюстрируется программой, решающей известную задачу, придуманную английским
физиком Полем Дираком: "Три рыбака легли спать, не поделив улова. Проснувшийся
ночью первый рыбак решил уйти, взяв свою долю. Но число рыб не делилось на три.
Тогда он выбросил одну, а из остатка забрал треть. Второй и третий рыбаки
поступили аналогичным образом. Спрашивается, какое наименьшее количество рыб
может удовлетворить условию задачи". Поль Дирак был мастер давать разным
существительным приставку "анти" ─ античастица, например. и в
этой задаче он, по-видимому, не изменил своей привычке, оригинально решив ее:
минус две рыбы. Выбрасываем одну – получаем минус три, забираем одну ─ получаем минус две и т.д.
Рис.0.9. Схема структурирования программы Рыбаки и рыбка
а) неструктурированная BASIC – программа
б) структурированная BASIC – программа
в) Pascal
– программа
Но в ответ вкралась ошибка, которую можно объяснить только,
наверное, тем, что у Дирака не было компьютера. На рис. 0.9 помещены программы
решения описанной задачи о рыбаках и рыбке простейшим методом перебора: берется
произвольное начальное число рыб (РО ─ см. строку 20) и
проверяется, удовлетворяет ли оно условиям честного дележа улова (цикл с
параметром на строках 80 -120). Если да ─ ответ готов (см. строку
140), нет ─
начальное число рыб уменьшается на единицу (см. строку 110), а расчет
повторяется. Рано или поздно машина ответ найдет. При этом ее, как и Поля
Дирака, не будет смущать тот факт, что количества рыб стали величинами
отрицательными. Нормального человека такой нюанс смутил бы, конечно, но
нормальный человек и до античастиц не додумался бы: талант ─ это
аномалия.
Но вернемся к нашим рыбакам и рыбкам. В программе, помещенной на
рис. 0.9, а, досрочное прерывание цикла с параметром "разваливает"
структуру. Спасти ее можно, введя в программу переменную-признак (FLAG ─ см.
строку 60 на рис. 0.9, б). Перед входом в цикл признак равен нулю. Если же хоть
раз остаток от дележа улова будет нецелым числом, то признак потеряет свое
нулевое значение (см. строку 110). Это будет сигналом к повторению счета с
уменьшенным на единицу числом рыб (см. строку 130). Программа на рис. 0.9, б
стала структурированной: в цикл "до" (см. строки 50-130) вложен цикл
с параметром (см. строки 80-120) с неполной альтернативой (см. строку 110).
Такую программу опять же несложно перевести на язык Паскль (рис. 0.9,в). В этом
языке под переменную-признак, принимающую только два значения, т.е. хранящую
один бит информации, зарезервирован особый тип данных ─
логический или булевский (BOOLEAN).
В программе на рис. 0.9, б использован также и прием дублирования:
начальное число рыб сначала увеличивается на единицу (см. строку 40), а потом
на эту же единицу уменьшается. Это тоже преследует цель структурирования
программы. Такой ход в программе подобен поведению прыгуна на разбеге: шаг
назад, а потом разбег.
Расчет по программам на рис. 0.9 дает не одно, а целый ряд решений
задачи:
..., -110, -83, -56, -29, -2, 25, 52, ...
Никто не будет спорить, что ─ 29, а тем более ─ 110 меньше, чем ─ 2. Да,
жаль, что у Дирака не было компьютера.
Читатель может попробовать решить задачу Дирака при других начальных
условиях ─ это
предусмотрено в программах (см. строки 10 и 30).
Непронумерованный совет. Решая задачу, закладывай в программу
возможность расширения и изменения начальных условий.
Задание читателю. Опираясь на вышеописанную методику
структурирования программ, попробуй сделать структурными неструктурные
программы книги.
Непронумерованный совет. Ставя своей целью написание
структурированной программы, не пытайся писать промежуточные эрзац-программы
без ясной структуры в надежде на дальнейшую их доработку. Старая немецкая
пословица утверждает, что нельзя правильно застегнуть мундир, неверно застегнув
первую пуговицу.
Структурирование BASIC-программ (см. рис. 0.8, а, б и 0.9, а, б)
ведет к некоторому снижению их быстродействия. Об этом побочном эффекте будет
рассказано в совете ОВ.
Задачу о рыбаках и рыбках можно решить намного быстрее, чем это
сделано по программам на рис. 0.9. Для этого за опорную точку, от которой
ведется приближение к ответу, нужно взять не начальное число рыб в улове, а
остаток от улова, достающийся последнему рыбаку. Вот как, например, будет
выглядеть рыбный ряд Дирака при пяти рыбаках, каждый из которых перед своим
уходом по-английски выбрасывает по одной рыбке:
... -6254/-2052, -3129/-1028, -4/-5, 3121/1020, 6246/2041...
Этот ряд (числитель ─ начальный улов, знаменатель
─ остаток
для последнего рыбака) подсказывает более быстрый алгоритм решения задачи
Дирака, чем заложенный в программы на рис. 0.9: за остаток рыб для последнего
рыбака берется единица, а потом к ней прибавляется по единице, если условие
дележа не выполняется. Но в этом случае, хоть задача и будет решена быстрее,
антирыб не получится: Дирак перешагнул нуль по своей гениальности, а машина,
работая по программам на рис. 0.9, ─ по своей
"наивности", не нарушенной приемами оптимизации.
"Описывай, не мудрствуя лукаво,
Все то, чему свидетель в жизни будешь..."
─
советовал отец Пимен Григорию Отрепьеву, наставляя его в искусстве летописи.
KISS-принцип искусства программирования (см. совет 41) требует, чтобы
поставленная задача решалась наиболее простыми алгоритмическими приемами.
Попытки ускорить счет или сэкономить память хитрыми приемами не только чреваты
ошибками, но и могут привести к обесцениванию результата.
При оформлении программы не пренебрегай графическими, цветовыми и
иными способами выделения структуры алгоритма.
Первые попытки выделения графическими средствами структуры
программы были предприняты А.Л. Брудно [5]. Но широкое распространение в то
время получили блок-схемы (см. рис. 0.6), а на листинг программы внимание
обращали мало. Основной недостаток блок-схем заключается в том, что они не
приучают к аккуратности при разработке алгоритма: ромб можно поставить в любом
месте блок-схемы, а от него повести выходы на какие угодно участки. Так можно
быстро превратить программу в запутанный лабиринт, разобраться в котором через
некоторое время не сможем даже сам ее автор.
Рис.0.10. BASIC
– программа
"Два шага": программа вписана в рамки структурной диаграммы
С появлением языков, отвечающих принципам структурного
программирования (Алгол, Pascal, Модула-2, Рапира, Си, Ада и др.), блок-схемы
стали отмирать. Структуру же программы начали выделять на ее листинге отступами
от левого края, причем чем больше глубина вложения функционального блока, тем
дальше отстоит он от левого края листинга. Такая манера выделения структуры
показана на рис. 0.10, где помещена программа определения локального максимума
многомерной функции вблизи заданной точки методом "Два шага" (модификация
метода Хука-Дживса [2]). Сама функция, аргументами которой являются элементы
одномерного массива Х, записана в заголовке программы процедурой (см. строку
8).
Вложение первого уровня: сама программа (см. строки 11-40).
Вложения второго уровня: ввод координат исходной точки (тело цикла
с параметром ─ см.
строку 13); расчет до достижения заданной точности поиска (тело цикла
"пока" ─ см.
строки 17-37); вывод ответа (тело цикла с параметром ─ см.
строку 40). Вложение третьего уровня: приближения к точке максимума измерением
степени подъема функции вблизи опорной точки (тело цикла "до" ─ см.
строки 19-35). Вложение четвертого уровня: перебор ординат функции в
положительном и отрицательном направлениях (тело цикла с параметром ─ см.
строки 22-34). И, наконец, вложение пятого уровня: запоминание точки
максимального подъема (правые плечи альтернатив ─ см. строки 26 и 32).
Рис.0.11. BASIC
– программа
"Два шага": "паскалевский" способ выделения структуры
программы
Программа, помещенная на рис. 0.11, решает ту же задачу о
максимуме многомерной функции, но написана она на BASIC'е.Программа получилась
более компактной помимо других причин и за счет того, что на BASIC'е допустима
вещественная переменная с любым шагом в качестве параметра цикла (см. строки
90-130 на рис. 0.11).
Многие BASIC-машины ради экономии своей памяти при трансляции
выкидывают пробелы, стоящие за номером строки. "Обмануть" машину в
ряде случаев удается, поставив знак-разделитель операторов сразу после номера
строки, как это и было сделано в программе, помещенной на рис. 0.11
(непронумерованный совет!).
Распространенность манеры выделения структуры алгоритма с помощью
пробелов породила специальные сервисные программы под названием "Красивая
печать": как угодно написанная прикладная программа распечатывается
машиной с нужным числом пробелов перед операторами.
Рис.0.12. BASIC
– программа
"Два шага": выделение структуры программы по А.Брудно
На рис. 0.12 BASIC-программа поиска максимума многомерной функции
вписана в структурную диаграмму. Такой способ выделения алгоритма приняли
авторы книги, и вот почему.
1. Программа на рис. 0.10 по форме напоминает стихи Маяковского.
Этот поэт публиковал свои произведения в то время, когда у нас еще не
чувствовался дефицит бумаги. Неизвестно, хватило бы у поэта в наше время сил
бороться с редакторами издательств за свой столь оформления стихов.
Делая программы компактными, мы, конечно, бумаги не сэкономили,
так как объем книги был заранее оговорен. Но благодаря этому приему в книге
оказалось больше программ. Это должно нас помирить с читателем, привыкшим к
традиционному выделению алгоритмов программами-лесенками (см. рис. 0.10).
2. Компактная программа полностью умещается на одном листе книги,
на одном экране дисплея. Это упрощает ее разбор, модернизацию. Ведь программа
на рис. 0.10 еле-еле поместилась на одной странице несмотря на свою
относительную простоту ─ на
строках 11, 14, 22, 26, 28 и 32 записано более одного оператора, что является
отступлением от правил "красивой" печати.
3. Структурные диаграммы позволяют ясно выделить алгоритм решения
задачи, написанный на любом языке программирования. Об этом подробно будет
сказано в совете 09.
Программа на рис. 0.12 отличается от аналогичной на рис. 0.11 тем,
что в ней числовые переменные были разнесены по двум типам: целочисленные (со
знаком процента) и вещественные двойной точности (со знаком диеза в конце имени
переменной). Такое разделение более отвечает сущности поставленной задачи и,
кроме того, повышает точность расчета. Швейцарский математик Н. Вирт вывел интересную
формулу: "Алгоритмы + структуры данных = программы", ставшую
названием книги [9].
Непронумерованный совет. Приступая к решению задачи одновременно с
разработкой алгоритмов (а может быть даже и раньше), опиши переменные и их
типы, которые будут задействованы в программе. Структурированные данные ─ это
наполовину решенная задача.
В этой книге упор сделан на раскрытие особенностей первого
слагаемого формулы Н. Вирта. Структуры данных в BASICе, на котором написано
большинство программ книги, развиты слабо.
В программе на рис. 0.12 (по сравнению с программой на рис. 0.11)
подпрограмма вычисления значения анализируемой функции "сползла" из
заголовка в "ноги". О месте подпрограммы в программах читай в совете
3В.
Структуру программы можно выделить и ремарками, отметив такую
особенность структурных диаграмм: цикл с параметром внешне напоминает букву П,
положенную на бок, циклы "пока" и "до" – прямой угол вершиной вниз
или вверх соответственно, а альтернатива-букву Т. Звездочками, объединенными в
ремарки, в программе на рис. 0.13 выделены все три вида циклов.
Рис.0.13. BASIC
– программа
"Два шага": выделение структуры программы с помощью комментариев
Рис.0.14. BASIC
– программа
"Нелинейная аппроксимация": выделение структуры программы цветной
печатью.
Аппаратные и программные средства современных компьютеров
позволяют создавать мягкие и твердые копии листингов программ с выделением
шрифтом структурных конструкций. Программа показана на рис. 0.14. По ней можно
методом наименьших квадратов аппроксимировать N пар точек на плоскости
полиномом степени U. Такая задача часто возникает, например, при статистической
обработке однофакторного эксперимента. Об алгоритме решения можно почитать в
[4, 29], мы же лишь отметим, что задача сводится к поиску корня (вектор Z)
системы U линейных уравнений с U неизвестными. Коэффициенты этой системы
формируются операторами строк 100 (свободные члены) и 120 (множители при
неизвестных), а ее решение находится довольно просто благодаря матричным
операторам: вычисление обратной матрицы (начало строки 160) и перемножение двух
матриц (второй оператор строки 160).
Цикл с параметром (а программу на рис. 0.14 можно назвать
вариацией на тему "цикл с параметром") был удачно определен в самой
первой версии BASICа и практически не претерпел никаких изменений в новых
версиях этого языка. Поэтому программу аппроксимации полиномом можно без
изменений использовать на любой BASIC-машине, знакомой с матричными
операторами. Но здесь может встретиться другой "подводный камень",
связанный с проблемой неопределенности возведения нуля в нулевую степень, если
один из аргументов исходной табличной зависимости будет равен нулю (см. строку
100 на рис. 0.14). Все BASIC-машины по характеру реагирования на такую ситуацию
можно поделить на две группы: один, руководствуясь правилами математики, выдают
сообщение об ошибке, другие же, руководствуясь принципом умолчания, выдают
единицу ─ к ней
обычно стремится неопределенность 00 в основной массе случаев. К этим случаям
относится и наш (см. программу "Линейная аппроксимация" на рис. 0.4).
Но большинство случаев
– это
еще не все случаи, поэтому "бойся данайцев, дары приносящих" и
особыми приемами (альтернативой, например) обходи неопределенности, как можно
реже доверяйся принципу умолчания.
Непронумерованный совет. Старайся не использовать одну и ту же
переменную под хранение разных величин. Такой прием был в широком ходу на заре
компьютеризации, когда экономии памяти машин придавалось огромное значение.
Сейчас, как правило, емкость памяти компьютера не является лимитирующим
фактором при решении задач, поэтому в программе на рис. 0.14 стоило бы под
исходную и обратную (инвертированную INV ─ см. строку 160) матрицы
коэффициентов системы отвести различные массивы.
В программе на рис. 0.14 в разных вариантах использована только
одна структурная конструкция цикла с параметром. Заголовки и концы этих циклов
пропечатаны красным цветом.
Можно придумать еще много различных способов выделения структуры
на листинге программы (см., например, совет 63), но мы ограничимся способами, проиллюстрированными
рис. 0.10, 0.11 и 0.12.
Функциональные блоки программы, вписанной в структурную диаграмму,
подобны кирпичам, скрепленным друг с другом с помощью растворов. Структурных
блоков не так уж много (мы насчитали шесть, хотя вопрос пока не закрыт ─ см.
советы 0А, 0Е и 0F), но из них удается построить сложные алгоритмические
конструкции. В этой связи можно провести такую аналогию. Многих поражает
разнообразие, сложность и великолепие готических построек. Тем не менее в
период расцвета этого стиля в архитектуре для их сооружения применялось строго
ограниченное число камней, высеченных из туфа, стандартизованной формы и
размера ─ из
единого каталога, как бы мы сказали теперь.
Формируя в программе функциональный блок, ограниченный
прямоугольными рамками, следи, чтобы у него был только один вход и только один
выход.
Функциональный блок на структурной диаграмме ─ это не
только бесцветный прямоугольник. Любой участок программы, ограниченный
прямоугольными рамками, а также вся сама программа ─ это
функциональные блоки разной степени вложения.
Рис.IIIa
Проиллюстрируем совет об одном входе и одном выходе программой
решения буквенной головоломки, которая когда-то была опубликована в журнале
"Квант":
USA
+
USSR
------
PEACE
Требуется определить, какие цифры скрываются за латинскими буквами
А, С, Е, Р, R, S и U (разные буквы обозначают разные цифры).
Рис.0.15. BASIC
– программа
"Головоломка":
а) без четкого выделения альтернатив
б) с выделением альтернатив
На рис. 0.15 помещены программы, решающие головоломку методом
прямого перебора.
Непронумерованный совет. Помни! Алгоритм решения задачи машиной
может в корне отличаться от алгоритма решения той же задачи человеком. У человека
есть смекалка, фантазия, интуиция, наконец. Машина может пока похвастаться лишь
точностью и быстродействием. Умей сочетать лучшие качества ЭВМ и человека.
Подробнее читай об этом в совете 4Е.
В программах на рис. 0.15 в строках 10 и 30 записаны операторы,
сформулированные после самого поверхностного анализа условий головоломки. Далее
задача решается последовательным присвоением неизвестным буквам А, S и С
значений 2, 3, 4, ..., 8. Если текущее сочетание букв удовлетворяет условию
задачи, то оно выводится на печать (см. строку 90).
Непронумерованный совет. Составляя программу, помни, что часто
задача может иметь не одно, а несколько решений. Вот почему в программах на
рис. 0.15 оператор печати помещен внутри циклов, а не после выхода из них.
Подробнее читай об этом в совете 4Е.
Операторы условного перехода на строках 40, 60 и 80, формирующие
неполные альтернативы, исключают совпадение чисел, соответствующих разным
буквам. На рис. 0.15, а эти альтернативы имеют по одному входу, но по два
выхода: один выход ─ через
нижнюю грань прямоугольника, а второй ─ через служебное слово THEN.
А это нехорошо (см. название совета). Дело в том, что при наличии только одного
входа и только одного выхода прямоугольные блоки программ легко изымать,
заменяя на новые в процессе отладки, модернизации. Нарушение этого правила
чревато ошибками. Программа, помещенная на рис. 0.15, б, дополнена ремарками
(строки 95, 105 и 115), замыкающими альтернативные блоки и объединяющими два выхода
в один.
Задание читателю. Попробуй решить описанную головоломку без
компьютера, а потом проверь на ЭВМ ─ единственно ли найденное
решение.
Непронумерованные совет. Не старайся решать задачи, написанные для
"головы", с помощью ЭВМ. Помни! Калькуляторы атрофировали у людей
навыки устного счета. Компьютеры же при сильном увлечении ими могут отучить нас
мыслить логически.
Изменив параметр цикла, можно досрочно выйти из него.
Совет иллюстрируется программами (рис. 0.16) решения известной
задачи об изобретателе шахмат, который попросил индийского раджу дать ему в
награду пшеницы, положив на первую клетку шахматной доски одно зерно, на вторую
─ два, на
третью ─ четыре,
на четвертую ─ восемь и
т.д., т.е. все время удваивая количество зерен (см. формулу на строке 30).
Раджа легкомысленно приказал слугам выдать награду, но, как оказалось при
подсчете зерен, это сделать невозможно.
Непронумерованный совет. Прежде чем самому или на компьютере
суммировать ряд числе, подумай, нет ли формулы, выражающей сумму ряда. Так, в
описанной задаче количество зерен на новой клетке шахматной доски равно сумме
зерен на предыдущих (1 + 2 + 4 + 8 + 16 + 32 + 64 = 128, например, если
вспомнить название нашей книги). Общее количество зерен на всей доске равно
количеству зерен, которое должно быть на 65-й, несуществующей клетке, что легко
определить и без суммирования, взглянув на строку 30 на рис. 0.16, а.
Рис.0.16. Программа "Зерно на шахматной доске":
а) BASIC
– программа
с досрочным выходом из цикла с параметром
б) Pascal
– программа
с изменением параметра цикла.
Представим себе, что слуги, отсчитав более миллиарда зерен,
вернулись к радже и сказали: "Хватит". Такую ситуацию имитируют
программы, помещенные на рис. 0.16. В BASIC-программе (см. рис. 0.16, а) цикл
отсчета зерен прерывается возгласом "Хватит" – переходом к метке,
поставленной вне цикла с параметром. В Pascal-программах метки нежелательны
(вспомни совет 00), поэтому в теле цикла (см. рис. 0.16, б) на правом плече
альтернативы переменной-параметру присваивается значение, превышающее
предельное.
Это заставит машину досрочно прервать цикл.
Непронумерованный совет. Пользуясь советом 04, помни, что
изменение параметра цикла в его теле ─ прием нежелательный, и вот
почему. Есть трансляторы, ревностно следящие за соблюдением всех правил
программирования. Такой транслятор "пронюхает" о самовольном
вмешательстве человека в переменную-параметр и даст сообщение об ошибке еще на
стадии ввода программы в ЭВМ.
Обрати внимание, что язык Pascal при всем своем совершенстве не
имеет такого элементарного и нужного действия, как возведение в степень. Эту
операцию приходится оговаривать специальной функцией пользователя (см.
заголовок программы на рис. 0.16, б).
Если ты затеял цикл с параметром, то умри, но выполни его до
конца.
Умирать, конечно, не стоит, но помнить о том, что досрочное
прерывание цикла с параметром в ряде случаев приводит к сбоям, нужно.
Рис.0.17. BASIC
– программа
сортировки числового массива: восстановление стека цикла с параметром.
Вот программа (рис. 0.17), сортирующая по простейшему алгоритму
одномерный числовой массив А в порядке убывания его элементов. Этот алгоритм был
нами уже использован в программе на рис. 0.5 для сортировки литерного массива.
Условным оператором на строке 70 (рис. 0.17) цикл с параметром прерывается, а
управление программой передается в заголовок цикла, минуя его конец на строке
80. На ряде BASIC-машин это приводит к аварийным остановам по программной
ошибке: "Два раза FOR без NEXT". В последних версиях BASICа это не
считается ошибкой, но такой прием все равно не рекомендуется: многократные
действия, общее количество которых заранее неизвестно, лучше реализовывать
через цикл "пока" или цикл "до" (см. также совет 3F).
В программу на рис. 0.17 вставлен оператор (строка 50),
подсказывающий машине, что происходит повторный вход в один и тот же цикл или в
новый цикл первого уровня (невложенный). Это не приводит к переполнению стека
FOR ─ NEXT.
Более совершенные трансляторы в такой ситуации разбираются сами без подсказок.
Они, кстати, также более простым и быстрым способом осуществляют обмен значений
у двух переменных ─ сравни
строку 70 на рис. 0.17 и строку 150 на рис. 0.5.
Непронумерованный совет. Вспоминай добрым словом системных
программистов, которые, не щадя своих сил, создают все более совершенные
трансляторы языков высокого уровня.
Никогда не используй значение параметра цикла по выходе из него.
Такая категоричность совета вызвана тем, что в разных трансляторах
после выполнения цикла его параметру присваивается разное значение. В наиболее
совершенных языках параметр цикла вообще считается как бы локальной переменной
(см. совет 5А), ликвидируемой после выполнения цикла: значение ее
неопределенно.
Обрати внимание на строку 50 программы решения системы линейных
уравнений методом Гаусса, помещенную на рис. 0.18. К такому сложному алгоритму
приходится прибегать [19], если в языке нет матричных операторов: взгляни на
программу на рис. 0.14, где такая задача решается двумя операторами строки 160.
Рис.0.18. BASIC
– программа
решения системы линейных алгебраических уравнений: использование параметра
цикла после выхода из цикла
Так вот, возвращаясь к сути нашего совета, следует отметить, что
слово REM в строке 50 на рис. 0.18 лишнее. Оно не исказит результат лишь в том
случае, если параметр цикла после выхода из него становится равным предельному
значению плюс (минус) шаг изменения, который в нашем случае по умолчанию равен
единице.
Непронумерованный совет. Если тебе хочется узнать, чему равен
параметр после окончания цикла на твоей BASIC-машине, то выполни программу
FORI = 1 TO 6 STE P2 : NEXT I : ? I.
Машины могут напечатать и 5 (последнее значение параметра,
умещающееся в оговоренных рамках цикла), и 6 (предельное значение параметра
цикла), и 7 (достигнутое значение параметра цикла) в зависимости от вкуса
(можно даже сказать каприза) программиста, писавшего транслятор для ЭВМ. На
вкусы же и капризы полагаться нельзя.
Непронумерованный совет. Формируя заголовок цикла, делай так,
чтобы параметр не "перешагивал" предельного своего значения.
Досрочно прервать программу можно, запрограммировав на одном плече
альтернативы аварийную ситуацию.
Читатель, взгляни еще раз на совет 03, где сказано, что каждый
блок программы должен иметь лишь один вход и один выход. Но есть ситуации,
когда нужно досрочно прервать программу в блоке с глубоким вложением. Следуя
совету 03, в такой ситуации нужно осторожно построить сеть логических переходов
для "всплытия" из глубины программы (от начала) на ее
"поверхность" (к концу). Эта осторожность подобна той, какая бывает у
водолазов, опасающихся кессонной болезни.
Рис.0.19. Pascal
– программа
поиска действительных корней квадратного уравнения: досрочное прерывание
программы аварийной ситуацией
На рис. 0.19 помещена программа линейной аппроксимации
однофакторной табличной зависимости методом наименьших квадратов. Задача
нелинейной аппроксимации решалась программой, помещенной на рис. 0.14. При
линейной аппроксимации нужно решать систему лишь из двух линейных уравнений с
двумя неизвестными. В программе на рис. 0.19 это делается методом Крамера [4,
29].
Непронумерованный совет. Не стреляй из пушки по воробьям. Если
тебе нужно решить систему из двух уравнений, не прибегай к алгоритмам,
рассчитанным на большее число уравнений.
Если детерминант D, высчитываемый по методу Крамера (рис. 0.19),
окажется равным нулю, то задача линейной аппроксимации решения не имеет.
Программу в этом случае можно прервать, "подсунув" машине деление на
нуль, например.
Непронумерованный совет. Давай переменным "говорящие", но
в то же время не очень длинные имена. Так, в программе на рис. 0.19 SX – это сумма значений Х, SY ─ сумма
значений Y, SXY ─ сумма
произведений Х на Y, а SX2 ─ сумма квадратов Х.
Потребность в досрочном прерывании выполнения программы вызвала
появление специальных операторов, которые можно вставлять в нужные участки
программы.
Непронумерованный совет. Дополняй операторы досрочного прерывания
программы комментариями, чтобы всегда было ясно, что явилось причиной сбоя.
Кроме того, помни, что операторы досрочного прерывания ─ это
атрибуты программ, написанных без цели сбыта. В программных продуктах они
недопустимы ─ см.
совет 1F.
Что за вопрос ? ─ подумает иной читатель. Это
же делается проще простого. Нужно переменной-сумматору присвоить нулевое
значение. Мы уже так поступали ─ см. рис. 0.14 и 0.19. Но!
Иная простота хуже воровства, если под воровством понимать неверное решение
машиной поставленной задачи.
При инициализации цикла сделай так, чтобы операции обнуления
сумматоров и другие оказались внутри цикла.
Рис.0.20. Pascal
– программа
"Линейная аппроксимация": обнуление сумматоров в теле цикла с
параметром
Программа на рис. 0.20 ─ это модернизация программы
линейной аппроксимации, помещенной на рис. 0.19. Что в программе изменено?
Во-первых, операторы обнуления сумматора оказались внутри цикла с параметром,
но при этом они работают всего лишь раз, при первом выполнении тела цикла,
когда I равно единице. Зачем это сделано? Если придется модернизировать
программу, дополняя ее возможностью повторного обсчета точек, то это (сумматоры
в цикле) убережет от повторного входа в цикл без его инициализации.
Второе отличие программы на рис. 0.20 от ее аналога на рис. 0.19
заключается в отказе от довольно-таки "скользкого" приема досрочного
прерывания программы аварийным остановом, в замене прерывания полной
альтернативой. Если продолжить образное сравнение программирования с водолазным
делом, начатое в предыдущем совете, то выход из программы на рис. 0.19 без
решения можно уподобить срочному подъему водолаза из глубины с последующим
помещением его в барокамеру, а выход из программы на рис. 0.20 ─ с
медленным подъемом по графику, исключающему кессонную болезнь.
Помещение операторов, обнуляющих сумматоры, в тело цикла (рис.
0.20) ─ решение не
самое оптимальное. Цикл "утяжеляется" операторами, работающими только
раз (об этом подробнее сказано в гл. 3). Лучшее решение поставленной проблемы
дано в программе нахождения экспоненты числа через суммирование ряда (рис.
0.21):
ex = N
xI_
I!
I=0 .
Факториал числа в программе вычисляется по формуле
I ! = (I ─ 1)! . I.
С учетом того, что факториал нуля равен единице. Следовательно, до
входа в цикл нужно не только обнулить сумматор, но и присвоить единицу
переменной FAK, хранящей значений I! Это и сделано на строке 20 непосредственно
перед заголовком цикла, расположенным тут же. Инициализация цикла при этом
ведется вне его, но это тем не менее, исключает повторный ход в цикл без
подготовки переменных, использующихся в его теле. Кстати говоря, заголовок
цикла с параметром на языке Си может включать в себя и операторы инициализации.
Непронумерованный совет. Используя при написании программ удачные
находки других языков программирования.
Рис.0.21. BASIC
– программа
вычисления суммы ряда: инициализация переменных в заголовке цикла с параметром
Задание читателю. Расшифруй "ребус" 1 = 1, помещенный на
строке 70 рис. 0.21. Ответ ─ в совете ОС.
Вписывай программы в структурные диаграммы. Это позволит выделить алгоритм,
освободив его от синтаксической оболочки конкретного языка.
Жан-Франсуа Шампольон открыл тайну египетских иероглифов с помощью
двуязычного камня, где один и тот же текст был высечен и по-древнеегипетски, и
по-древнегречески. Мы надеемся, что четырехлингва, помещенная на рис. 0.22,
поможет читателям, знающим один язык, начать осваивать новый.
Рис.0.22. Трилингова Fortran – Pascal – сортировки
числового массива методом пузырька
Переходя от обсуждения языка программирования к обычному языку,
можно отметить, что человек по-настоящему начинает понимать и ценить свой
родной язык только после знакомства с одним из иностранных. Многие филологи
высказываются еще более категорично в том плане, что знание лишь одного языка ─ это
незнание никакого. Хотя эта книга и обращена к начинающим программистам, но ...
плох тот начинающий программист, который не хотел бы стать опытным.
Программист-профессионал должен знать в совершенстве три-четыре языка и иметь
общее представление еще минимум о десяти. Этой истиной один из авторов книги
переубедил другого, когда у них возник спор о том, стоит ли в нее включать
ведения о других языках программирования помимо BASICа. Да и сам BASIC имеет
диалекты, которые так отличаются друг от друга, что их можно было бы отнести
даже к разным языковым группам.
Четырехлингва на рис. 0.22 сортирует числовой массив методом
пузырька [27]. По ней читатель может проследить, как реализуются основные
программные конструкции (задание массивов, ввод значений переменных, циклы,
альтернативы, операторы присвоения, вывод на печать и др.) на самых популярных
языках программирования.
Не пытайся усомниться в основной структурной теореме ─ она
верна.
Достаточно ли при программировании иметь под рукой лишь шесть
управляющих конструкций, описанных в совете 00? Верна ли основная структурная
теорема, гласящая, что любой алгоритм можно реализовать через следование
(последовательное выполнение функциональных блоков), повторение (три вида
циклов) и выбор (альтернативы и множественное ветвление). Но перечисленный
список из шести позиций можно и увеличивать, и уменьшать.
Вернемся в начало книги к программе поиска корня алгебраического
уравнения методом Ньютона (рис. 0.3). Некоторых читателей, наверно, удивил тот
факт, что значение начальной точки поиска сначала присваивается переменной Х
(см. строку 30), а потом тут же пересылается в переменную Х1 (см. строку 50).
Без такой маленькой хитрости в этой программе нельзя было бы использовать цикл
"до". Алгоритм поиска корня методом Ньютона без хитростей требует
новой структурной конструкции: цикл с выходом из середины, который задействован
в программе на рис. 0.23, а.
Рис.0.23,а. BASIC
– программа
решения алгебраического уравнения методом Ньютона: цикл с выходом из середины
Рис.0.23,б. Фагмент программы решения алгебраического уравнения
методом Ньютона, написанной на языке Модула: цикл с выходом из середины
На языке Pascal эта
конструкция выполнима только с использованием метки ─
нежелательного элемента программирования. В язык Модула-2, который Н. Вирт
создал вслед за языком Pascal, был включен цикл с выходом из середины,
по-видимому, для того, чтобы совсем устранить потребность в метке (рис. 0.23,
б). Язык Модула-2, как, впрочем, и некоторые диалекты Паскаля, понятия метки не
имеет.
Итак, мы показали, что управляющих конструкций может быть больше,
чем шесть. А нет ли среди этих шести лишних?! Есть. Цикл "до",
например. Его несложно заменить циклом "пока". Цикл "пока"
отличается от цикла "до" лишь тем, что тело цикла "пока"
может быть не выполнено ни разу, в то время как тело цикла "до"
выполняется как минимум один раз. Вот и все отличие! От цикла "до"
можно всегда отказаться, смоделировав при необходимости условия для первого
входа в тело цикла (что-то подобное происходит и в футболе, когда защитники
разыгрывают искусственную ситуацию "вне игры", пропуская нападающего
одного к воротам).
Рис.0.24. BASIC
– программа
решения алгебраического уравнения методом Ньютона: цикл "пока"
В программе на рис. 0.24 (поиск корня методом Ньютона) таким
способом цикл "до" заменен циклом "пока". Для этого
переменной Х1 было присвоено значение, заведомо отличающее ее от переменной Х
на величину, большую заданной точности Е (см. строку 4).
Вот еще один типичный пример цикла с выходом из середины – запрос значения переменной с
повтором, если введенное число не умещается в оговоренном интервале (рис.
0.25).
Рис.0.25. Участок BASIC – программы ввода значения переменной с контролем:
а) цикл с выходом из середины
б) цикл пока и альтернатива
Программа на рис. 0.25, а запрашивает число (см. строку 10),
проверяет его правильность (см. строку 20) и повторяет запрос в случае ошибки, сопроводив
его комментарием и звуковым сигналом (см. строку 30). От цикла с выходом из
середины можно избавиться (рис. 0.25, б), заменив его на цикл "пока"
со встроенной альтернативой и воспользовавшись методами структурирования
(дублирование и использование признаки), описанными в совете 01.
Цикл "пока" можно также заменить на цикл "до",
но при этом в ряде случаев придется воспользоваться неполной альтернативой,
исключающей при необходимости выполнение тела цикла (сравни программы
сортировки массивов на рис. 0.4 и 0.26).
Рис.0.26. BASIC
– программа
сортировки вводимого с клавиатуры числового массива: замена цикла пока на цикл
до и альтернативу
Непронумерованный совет (подражание Козьме Пруткову). Если у тебя
спросят, что важнее ─ цикл
"пока" или цикл "до", ответствуй: цикл "пока".
Без цикла "до" можно обойтись. Без цикла "пока" обойтись
труднее. Замена его на цикл "до" требует дополнительной альтернативы.
К вопросу об оптимальном числе управляющих конструкций
программирования мы еще вернемся в советах 0Е и 0F.
Ради дела можно отказаться даже от самых модных концепций
программирования.
"Париж стоит мессы", ─ сказал Анри IV, принимая
католичество и вступая на французский престол.
"Для повышения быстродействия программы можно пойти и на
потерю ее структурированности", ─ сказал программист имярек,
изменяя пару операторов в программе.
\
Рис.0.27. Трилингова решения алгебраического уравнения методом
половинного деления:
а) язык програмируемого микрокалькулятора
б) BASIC
в) Pascal
г) неструктуированная ,но "быстая" BASIC – программа
Такой, несколько конформистский прием, иллюстрируется трилингвой
(язык микрокалькулятора, BASIC и Pascal ─ рис. 0.27) решения
алгебраического уравнения методом половинного деления, когда интервал неопределенности
А-В делят пополам и проверяют, в какой половине находится корень. Эта половина
становится новым интервалом неопределенности, а вся процедура повторяется до
тех пор, пока не будет достигнута заданная точность расчета.
Программа на рис. 0.27, б написана на неструктурной версии BASIC'а
где цикл "пока" и альтернативы реализуются операторами условного и
безусловного переходов. В этой программе можно отметить лишний переход: со
строки 28 на строку 34, а с нее на строку 8 ─ в заголовок цикла
"пока". Программа на рис. 0.27, г от таких излишеств освободилась, но
за это она лишилась и структуры. Такой потери можно избежать. Пример тому
программа на рис. 0.28. Она заинтересует химиков-аналитиков, если таковые
окажутся среди читателей книги: программа позволяет определить эмпирическую
формулу вещества (С2Н601, например) по данным анализа (массовые доли
элементов).
Рис.0.28. BASIC
– программа
определения формулы химического соединения по данным анализа
В программу (ее автор В.
Васильев из Орла) заложен алгоритм Евклида поиска наибольшего общего делителя
(НОД) многих чисел: сначала находят НОД двух чисел, далее находят НОД
полученного числа и третьего из данных чисел и т.д. до последнего числа. Так
как массовые доли элементов всегда определяются с некоторой погрешностью, то
НОД чисел рассчитывается за 10 попыток с разной точностью (см. цикл с
параметром на строках 70-190). Нехимики могут все эти объяснения опустить,
обратив внимание только на строки 110-140: вот как можно записать цикл
"пока" со встроенной альтернативой, сохранив и быстродействие
программы, и ее структуру.
Непронумерованный совет. Не допускай, чтобы машина запрашивала у
пользователя значения физических констант. Так, в программе на рис. 0.28 запрос
атомных масс элементов лучше заменить на запрос символа элементов (0, Н, С, Сu
и т.д.), по которым машине несложно "вспомнить" их характеристики.
Структуру программы можно подчеркивать неструктурными средствами.
Очень часто короткие программы заканчивают оператором безусловного
перехода в ее начало. Это позволяет такую программу использовать вновь и вновь,
вводя новые исходные данные. Такая программа имеет один вход и ни одного
выхода, что (см. совет 03) является грубым нарушением правил структурного
программирования. Тем не менее подобную программу можно полностью
структурировать, что и сделано в программе на рис. 0.29 ─ в
программе линейной аппроксимации однофакторной табличной зависимости (см. также
рис. 0.19 и 0.20).
Рис.0.29. Pascal программа "Линейная аппроксимация":
один вход – ноль
выходов
Условие, записанное в конце программы на рис. 0.29, никогда не
выполнится (фольклорная форма описания такой ситуации: "Когда рак на горе
свистнет"), поэтому программа теоретически может работать до
бесконечности. Но на практике, конечно, этого не бывает ─ прогон
подобных программ прерывается нажатием специальных клавиш клавиатуры ЭВМ. Но
чаще подобный замкнутый круг разрывают иначе ─ вставкой в него участка
запроса на продолжение работы по программе.
Во избежание лишних меток в программе заменяй полную альтернативу
на две неполные.
Этот совет сформулирован Д. Ван Тасселом [8] в отношении языка
fortran IV, где неполную альтернативу можно записать без опоры на метку
(взгляни на программу на рис. 2.7, а). То же можно сказать и об основных
версиях BASIC'а (см. строки 50 и 60 на рис. 0.8, б). Полная альтернатива и на
BASIC'е, и на fortran'е реализуется через обращение к меткам (см. строки 24-32
на рис. 0.27, б).
Рис.0.30. BASIC
– программа
"Орел – решка":
две неполные альтер
нативы вместо одной
В программе игры с машиной в "Орел или решку" (рис.
0.30) полная альтернатива (рис. 0.30, а) заменяется на две неполные (рис. 0.30,
б). При этом первая неполная альтернатива (строка 40 на рис. 0.30, б) содержит
условие и правое плечо, а вторая (строка 50) условие с обратным знаком и левое
плечо. Так довольно просто можно убрать из программы некоторые метки.
При особом желании из программы можно убрать все альтернативы,
будь они полные или неполные.
Мы, право, не знаем, у кого и при каких обстоятельствах может
возникнуть такое желание, но если оно все-таки возникнет, то осуществить его
можно очень просто, буквально в два шага (рис. 0.31.).
Рис.0.31. Схема замены полной альтернативы на два цикла
"пока"
Шаг первый. Полная альтернатива заменяется на две неполные. Об
этом рассказано в предыдущем совете.
Шаг второй. Неполная альтернатива заменяется на цикл
"пока", тело которого либо выполняется раз, либо не выполняется ни
разу. Такое специфическое поведение цикла "пока" поясним примерами.
Рис.0.32. Pascal
– программа
поиска действительных коней квадратного уравнения:
а) с полной альтернативой
б) с двумя циклами "пока"
Программу поиска действительных корней квадратного уравнения (рис.
0.32) можно назвать классическим примером полной альтернативы, кочующим из
одного учебника по программированию в другой. Программой на рис. 0.32, а это
уравнение решается традиционным способом с использованием альтернативы. Ввод в
программу логической переменной Р (эта операция ─ один из двух методов
структурирования, описанных в совете 01) позволил полную альтернативу заменить
на два цикла "пока" (см. рис. 0.32,б). В момент выполнения тела
любого из двух циклов переменная Р меняет свое значение на противоположное, что
исключает зацикливание программы.
Рис.0.33,а. BASIC
– программа
поиска минимума функции методом золотого сечения: цикл "пока" и
полная альтернатива
Рис.0.33,б. BASIC
– программа
поиска минимума функции методом золотого сечения: два цикла "пока"
Программа (рис. 0.33) поиска минимума одномерной функции методом
золотого сечения [2 и 19] в теле цикла "пока" содержит альтернативу
(рис. 0.33,а), которую также несложно преобразовать в два цикла
"пока" (рис. 0.33,б), меняя значение переменной Р, логической по сути
и числовой по форме.
Отсюда вывод: третий элемент в основной структурной теореме
(следование, повторение, выбор) ─ лишний.
Да, можно.
В самом первом совете книги мы сравнили программирование с игрой
на семиструнной гитаре. Потом заметили, что переход к метке не нужен и даже,
как считают многие теоретики программирования, вреден ─ с таким
же успехом играют и на шестиструнной гитаре. Какое же минимальное количество
"струн" можно оставить в программировании? Говорят, Паганини смог
доиграть свою виртуозную пьесу даже после того, как на ней оборвались три
струны. Попробуем оборвать "струны" программирования.
Седьмую "струну" (переход к метке) мы убрали еще в
совете 01.
Шестая "струна" (вызов процедуры или подпрограммы) также
легко
обрывается, если принять, что в процедуру выносятся повторяющиеся
участки программы и только. Но об этом мы поговорим еще особо в конце совета.
Цикл с параметром (пятая "струна") является не чем иным,
как циклом "пока" со счетчиком в теле (см. изображение цикла
"пока" блок-схемой на рис. 0.6).
Транслируя программу с циклом с параметром, машина переводит его
либо в цикл "до" ("Искра 226"), либо в цикл
"пока" ("Электроника Д3-28"). Эти машины дадут разный ответ
при выполнении конструкции
FOR 1=2 ТО 1:PRINT I:NEXT
"Искра" выполнит один раз тело этого цикла с неверным
заголовком, а "Электроника" ─ ни разу.
Непронумерованный совет. Во избежание неопределенностей избегай
противоречий в записи заголовка цикла с параметром.
Цикл "до" (четвертая "струна") также легко
заменим на цикл "пока". Об этом было сказано в совете 0А.
Третья "струна" (множественное ветвление), как было уже
отмечено, эквивалентна цепочке альтернатив, а саму альтернативу (вторая
"струна") несложно заменить на два цикла "пока" (вспомни
совет 0Е).
Итак, на скрипке с одной струной играть можно, и эта
"струна" – цикл
"пока". А раз можно, то "сыграем".
Одна из первых программ книги, где были задействованы все (кроме
множественного ветвления) управляющие конструкции, ─ это
программа поиска максимума многомерной функции методом "Два шага"
(см. рис. 0.10-0.13). Переписывание ее с использованием только цикла
"пока" дало результат, представленный на рис. 0.34.
Рис.0.34. BASIC
– программа
"Два шага": использование только циклов пока
При этом следует отметить,
что замена неполных альтернатив на циклы "пока" (сравни строки 9 и 11
на рис. 0.12 со строками, 110, 120 и 140, 150 на рис. 0.34) не потребовала
дополнительных логических переменных, как это случилось в программах на рис.
0.32 и 0.33. Дело в том, что в программе на рис. 0.12 действия, записанные в
правых плечах альтернатив (строки 9 и 11), меняют логическое условие
заголовков. Такая альтернатива как бы сама просится, чтобы ее переделами в цикл
"пока".
Осторожно!!! К подобной "просьбе" альтернативы, если она
полная, следует относиться с опаской. Если в плече альтернативы производятся
действия, изменяющие значение ее заголовка (как, например, в программе
"Два шага"), то неаккуратная замена такой полной альтернативы на две
неполные чревата ошибкой ─ см. совет 33.
Все семь управляющих конструкций были задействованы в программе
"Частотный словарь", помещенной на рис. 0.5. Вот как будет выглядеть
эта программа, если ее переписать с использованием только цикла
"пока" на неструктурной (рис. 0.35) и структурной (рис. 0.36) версиях
BASICа.
Рис.0.35. BASIC
– программа
"Частотный словарь": сверхструктурированная программа на неструкурной
версии языка
Рис.0.36. BASIC
– программа
"Частотный словарь": сверхструктурированная программа на структурной
версии языка
Мы, конечно, не призываем
читателя оставить на скрипке или гитаре только одну струну, т.е. при написании
программ использовать только цикл "пока". Программы на рис. 0.35 и
0.36 просим рассматривать как некие курьезы в духе названия данной главы. Сама
же программа "Частотный словарь" независимо от того, каким стилем ее
напишут, очень полезна.
Непронумерованный совет. Если ты набрал на ЭВМ текст, то перед
чистовой распечаткой не поленись составить и проанализировать его частотный
словарь. Если при этом в тексте, например, девять раз встречается слово
"компьютер", а один раз ─ "компьюрер", то
ясно, что Р ─ это
опечатка, которую нужно исправить. Если же в тексте всего к примеру 100 слов,
то десятикратное использование слова "компьютер" свидетельствует о
скудости словарного запаса у человека его написавшего.Этому слову нужно
подобрать синонимы – ЭВМ,машина
и т.д.
На рис.0.37 помещен частотный словарь программы "Частотный
словарь",написанной на неструктурной версии BASIC'а с использованием
только цикла "пока" (см.рис.0.35).
Рис.0.37. Частотный словарь программы "Частотный
словарь" помещенной на рис.0.35:
а) по частоте встречи слов;
б) по алфавиту;
Задание читателю.Ответь, почему в программе на рис.0.35 слово IF
было использовано 15 раз, а слово GOTO ─ 14, т.е. на единицу меньше?
Ведь, как мы знаем, цикл "пока" реализуется одним оператором
условного перехода (со словом IF) и одним оператором безусловного перехода (со
словом GOTO). Ответ ─ в совете
39.
От подпрограммы перестановки местами соседних слов частотного
словаря, помещенной на строке 150 (см. рис. 0.5), мы избавились в программах
0.35 и 0.36 довольно просто, продублировав ее в двух местах. Но есть ситуации,
когда подпрограмму (процедуру) так просто не исключить. Они возникают при
использовании так называемых рекурсивных алгоритмов, когда процедура еще не до
конца определена, а уже сама себя вызывает. Рекурсия задействована в программах
на рис. 0.38 и 0.39.
Рис.0.38. PASKAL-программа определения факториала и чисел
Фибоначчи: использование рекурсии
Рис.039. BASIC-программа реализации игры "Ханойские
башни": использование рекурсии
Первая программа позволяет
рассчитать числа Фибоначчи (1, 1, 2, 3, 5, 8, 13 и т.д.) и факториалы (1, 1, 2,
6, 24, 120, 720 и т.д.), а вторая – решить известную задачу о Ханойских башнях (автор решения Фан Ван
Чиен). Подробнее об этой задаче можно прочитать в [15] и в совете 56, сейчас же
мы только отметим, что при ее решении нужно перенести диски пирамиды (Ханойской
башни) со стержня А на стержень С, беря их по одному и пользуясь промежуточным
стержнем В. При этом нельзя большой диск класть на маленький. На исходном
стержне диски лежат в таком порядке: самый крупный ─ внизу, а
самый маленький ─ наверху.
Задача с двумя дисками решается просто: А ─ В, А ─ С и В ─ С. При
трех дисках последовательность действий такая: А ─ С, А ─ В, С ─ В, А ─ С, В ─ А, В ─ С и А ─ С. Число
действий в общем случае равно 2 в степени N-1, где N ─ число
дисков. Внимательный читатель может отметить, что задачу о перестановке N
дисков можно упростить, сведя ее к серии задач о перестановке N-1 дисков.
Отсюда и рекурсия в программе на рис. 0.39.
Более корректно проблему числа "струн" программирования
можно решить так. Любую программу можно написать, пользуясь только циклом
"пока" и не задействуя рекурсивные алгоритмы. В противном случае
"струн" у программирования две: цикл "пока" и вызов процедуры.
Вопрос о необходимости обращения к рекурсиям остается открытым. Есть мнение,
что этот метод направлен на упрощение восприятия программы человеком за счет
существенного усложнения реализации программы компьютером. Описанные задачи
(см. рис. 0.38 и 0.39) и без рекурсии решаются более эффективно: см. рис. 0.21
(факториал), рис. 3.4 и 3.5 (числа Фибоначчи) и рис. 5.6 (задача о Ханойских
башнях).
Непронумерованный совет. Помни! Вечный спутник рекурсии ─
альтернатива (или пара циклов "пока"). Попробуй выполнить на машине
простейшую рекурсивную программу без альтернативы
1 GOSUB 1
и посмотри, что из этого получится.
Педантичный читатель отметит, что в вышеприведенной пограмме на
рис.0.1. более уместен цикл с выходом из середины, а не цикл "пока".
На языке Pascal, его так просто не запишешь. При необходимости он реализуется
двумя способами. Во─первых,
прервать цикл в любом месте можно с помощью метки и оператора условного
перехода (If...Goto...). Те же, на кого метка в программе действует как красная
тряпка на быка, как говорил Даниил Хармс, "поступают самым остроумным
способом". Тело цикла записывается процедурой с вкраплениями If...Then
Exit ─ если...,
то прерви выполнение процедуры и вернись в основную программу – к оператору, записанному за
именем процедуры. На QuickBASIC'е цикл можно прервать в любом месте любое число
раз (DO...IF...THEN EXIT DO...LOOP или FOR...IF...THEN EXIT FOR...NEXT). На
языке C пошли еще дальше: цикл можно прервать, передав управление программой
либо в конец цикла (If...Then Break), либо его начало (If...Then Continue). Так
что можно быть "более католиком чем папа римский" и более
структурированным языком чем Pascal.
Если наш
читатель еще не совсем запутался, то вот какую "ясность" в проблему
минимального числа управляющих конструкций алгоритмов (суть основной
структурной теоремы) внесла переведенная с английского и выпущенная в 1989 году
издательством "Мир" книга "Языки программирования Ада, Си и
Pascal Сравнение и оценка" [Д1]. В ней на страницах 72 и 73 сказано:
"В соответствии с генеалогией управляющих структур <...>, циклы с
использованием оператора завершения Break и оператора продолжения Continue
относятся к классу DREC(1). Теоремы, доказанные Р.Косараю, показывают, что
управляющие структуры, относящиеся к классу DREC(1), не могут быть эмулированы
(т.е. "сведены к" ─ прим. ред.) с помощью управляющих структур, относящихся к классу
D (название этого класса образовано от первой буквы фамилии Э.Дейкстры ─ автора основной структурной
теоремы), которые составляются из произвольного числа условных операторов If,
операторов цикла While и их конкатенаций (т.е. сочетаний, вложений друг в друга
и т.д. ─ прим. ред.). На самом деле
управляющие структуры, относящиеся к классу DREC(i) (имеющие в своем составе
оператор выхода Exit(i) (или Break), обеспечивающий выход на i уровней вверх, и
оператор Cycle(i) (или Continue), обеспечивающий выполнение цикла на i-м уровне
вложенности, как, например, в языке Блисс), являются более мощными, чем
управляющие структуры, относящиеся к классу DREC(i─1). <...>. Однако программы,
содержащие циклы с использованием операторов Exit и Cycle, оказываются намного
сложнее для понимания. Анализ показывает, что необходимость использования
оператора Exit возникает крайне редко. Необходимость наличия управляющих структур
более высокого уровня, чем управляющие структуры класса D до сих пор не
доказана." Вот так─то.
Начали "за упокой" основной структурной теоремы, а кончили ее
"за здравие".
Заставка 1
Можно ли сыграть по переписке в игру "Угадай число" (см.
рис. 0.8)? А почему бы и нет. Игра, правда, получится скучная, но ведь играют
же по переписке в шахматы.
Общение пользователей с ЭВМ первых поколений велось как бы по
переписке. Человек передавал оператору ЭВМ колоду перфокарт с пробитыми на них
условиями задачи и программой ее решения. оператор составлял пакет из таких
колод, машина же решала задачи одну за одной. "Ни секунды простоя!" – такой лозунг витал в
машинном зале. Но тем не менее эффективность пакетного режима работы
компьютеров была низкая, так как пользователь не видел промежуточных
результатов и не мог оперативно вмешаться в ход расчета.
Диалоговый режим работы с компьютером, внедренный сейчас
повсеместно, позволяет оптимальным образом сочетать лучшие качества человека и
машины. Вот почему проблеме организации удобного (дружественного, как говорят
программисты) диалога с компьютером посвящена одна из восьми глав книги.
Перед выводом новой информации очищай дисплей.
При выводе большого объема информации на дисплей курсор выписывает
строчку за строчкой, перепрыгивая на нижние позиции. Когда курсор окажется на
самой нижней строке, то начнут перепрыгивать вверх ранее выведенные строки. Это
ухудшает восприятие информации, да и плохо влияет на зрение. Поэтому всегда
предваряй вывод информации на дисплей его очисткой. Так, кстати, поступает
любой мало-мальски опытный лектор, откашливаясь перед очередной фразой.
Рис.1.1. BASIC
– программа
решения квадратного уравнения: очистка экрана
В программе полного решения квадратного уравнения (рис. 1.1)
первый оператор очищает дисплей и переносит курсор в левый верхний угол экрана.
Код этой операции – 03.
Четыре непронумерованных совета.
1) Раскрывай комментарием смысл редких или "неговорящих"
операторов (строка 5 на рис. 1.1).
2) Организуя диалог, предусмотри возможность обработки машиной
даже самых на первый взгляд невероятных комбинаций исходных чисел: А=0, В=0 и
С=0, например.
3) Переходя к программированию на новом языке, будь осторожен! Может
оказаться, что какое-то слово в новом языке имеет противоположный смысл: SQR на
Бейсике – это
призыв извлечь квадратный корень (строка 70 на рис. 1.1), а на Паскале – возвести в квадрат (см. рис.
0,27, в).
4) Записывай выводимые на печать величины в переменные. Вдруг они
пригодятся!
Последний совет не выполнен в программе на рис. 1.1 потому, что
Бейсик, как правило, не имеет переменных комплексного типа. В таком языке, как
Фортран, они есть.
Операции управления курсором очень важны при организации диалога
машины с человеком. Поэтому мы их опишем подробнее на примере программы (рис.
1.2), реализующей известную задачу о развитии колонии микроорганизмов, которую
придумал Дж. Г. Конвэй [15].
Рис.1.2. BASIC-программа "Эволюция жизни": управление
курсором дисплея
Краткие ее условия. Поле, где живет колония, поделено на клетки, в
каждой из которой может жить одна особь, окруженная соседями числом не более
восьми. Если число соседей более трех или менее двух, то особь погибает от
перенаселения или одиночества соответственно. В противном случае она переходит
в следующее поколение (см. строку 130 на рис. 1.2). На пустом месте может
возникнуть новая особь, если это место окружают три микроорганизма (см. строку
150). Программа на рис. 1.2 после задания начальной конфигурации колонии (см.
строки 10 и 20) моделирует ее развитие, вырисовывая на дисплее очертания новых
поколений. В программе использованы следующие операторы управления курсором:
1) перенос курсора в левый верхний угол экрана (см. строку 40);
другое написание этого оператора – НОМЕ, т.е. "домой!";
2) то же с очисткой экрана (см. строку 110); за точку отсчета, как
правило, принимают "дом" (НОМЕ) курсора – левый верхний угол, имеющий координаты Х=0 и Y=0.
Не сложно на экране дисплея стереть только часть информации – взгляни на первый оператор
строки 150, печатающий пробел.
На Бейсике можно всегда узнать местоположение курсора. Для этого
существуют специальные встроенные функции без параметров, вызывающие абсциссу и
ординату курсора или маркера, как его еще называют.
Задание читателю. Если у тебя цветной дисплей, то сделай так,
чтобы цвет выражал возраст особи. В программе на рис. 1.2 этот возраст
отмечается цифрами от 1 до 9.
Непронумерованный совет. Совмещай раскрытие комментарием сущности
переменной с описанием производимых над ними действий (см. строки 70 и 160 на
рис. 1.2).
Курсор может увеличивать свои размеры, охватывая собой целые фразы
программного "меню", например. При этом выбор нужного режима может
вестись довольно просто
– передвижением
клавишами клавиатуры или "мышью" курсора на нужное слово и нажатием
клавиши "Ввод".
Дублируй ввод значений важных переменных.
Ошибка ввода всегда нежелательна, но можно назвать ситуации, когда
она приведет к необратимым последствиям. Такое случается, если машина управляет
технологическим процессом и обратилась к человеку за советом. Для подобных
случаев и рекомендован двойной ввод значения переменной, реализованный
программой на рис. 1.3.
Рис.1.3. Фрагмент BASIC-програмы ввода числа с проверкой
Одно и то же значение присваивается разным переменным (А и А1), а
при их несовпадении запрос повторяется. При этом важно, чтобы человек не видел
первого значения (см. конец строки 10), а то он будет смотреть на него, а не на
первичный источник информации и ошибется повторно.
Задание читателю. На строке 40 на рис. 1.3 записаны два
шестнадцатеричных числа: 03
– код
очистки дисплея и 07 –
код
звукового сигнала. Эта строка аудиовизуально обращает внимание пользователя на
ошибку. Правилен ли порядок чередования кодов 0307? Не стоит ли их переставить
местами?
Прием двойного ввода информации широко применяется в устройствах
телекоммуникации для контроля за искажениями. Часто, например, ЭВМ, посылая
байт информации периферии, дополняет его контрольным байтом, сумма которого с
первым дает определенное сочетание – FF, к примеру. Маловероятно, что оба байта исказятся так, что
сумма их останется равной оговоренному числу.
Непронумерованный совет. Организуя ввод архиважной информации,
можно потребовать от человека выполнения каких-то дополнительных действий:
ввести, например, исходное число, а вслед за ним – его гиперболический арккосинус.
На вкус и цвет товарищей нет. Но приемы удобного и быстрого ввода списков
в память ЭВМ товарищу порекомендовать можно.
Ввод списков в память ЭВМ – одна из самых распространенных и нудных операций. Ввод программы
через клавиатуру машины
– это
по сути тот же ввод списка литерных переменных. Вот почему четыре последующих совета
(12-15) будут затрагивать эту тему.
Непронумерованный совет.
Вводя в ЭВМ список (программу), подсунь лист под клавиатуру и
передвигай его по мере ввода строк, указывая краем клавиатуры очередную
позицию. А вообще-то, научись у опытных машинисток работе с рукописями,
используй специальные пюпитры.
Перед вводом в ЭВМ элементов списка не поленись пересчитать их и
сообщить полученную сумму машине. Это позволит по завершении ввода быть
уверенным, что ни один элемент не пропущен и не введен дважды. Ведь вероятность
сделать по одному разу и первую (пропуск элемента), и вторую (повторный ввод)
ошибку очень мала.
Алгоритм такого ввода элементов списка, запоминаемых машиной в
виде одномерного числового массива А, реализован программой на рис. 1.4.
Рис.1.4. BASIC -программа ввода элементов массива: окончание ввода
по сигналу
Пользователи часто не то чтобы ленятся пересчитывать элементы
списка, но тем не менее перекладывают эту работу на плечи машины, договариваясь
с ней о том, что после ввода последнего элемента будет подан какой-то
оговоренный сигнал. Так было предусмотрено при составлении программы,
представленной на рис. 1.5, работая по которой, можно заполнить весь (или
часть) массив А ненулевыми значениями. Ввод нуля на запрос машины означает
конец заполнения массива.
Рис.1.5. BASIC-программа ввода заанее пересчитанных элементов
массива
Программа на рис. 1.4 имеет еще одно преимущество перед программой
на рис. 1.5. Машина, работая по программе на рис. 1.4 и заранее зная о размере
списка, отводит под его элементы ровно столько места в памяти, сколько нужно.
Согласно программе на рис. 1.5 это делается с запасом в надежде на то, что
тысячи ячеек будет достаточно для размещения вводимой информации. Так можно
экономить память компьютера. С другой стороны, как это бывает в жизни, где
недостатки – это часто
продолжение достоинств и наоборот, программа на рис. 1.5 позволяет ввести в ЭВМ
пропущенный элемент списка, так как массив в ней зарезервирован как бы
навырост, а не впритык (см. рис. 1.4). Так что, как поется в песне,
"думайте сами, решайте сами – иметь или не иметь...", т.е. пересчитывать заранее элементы
списка или нет.
Если ты решил пересчитывать элементы вводимого списка, то не
поленись также пронумеровать их. А программы дополни печатью номера затребованного
элемента, как это сделано на рис. 1.4 и 1.5. Вероятность ошибок ввода будет еще
меньше.
Фиксируй минимальное и максимальное значения элементов
вводимого числового массива.
Знание диапазона изменения поможет в ряде случаев избежать
характерных ошибок – пропуска
десятичной точки, знака мантиссы или порядка, например.
Реализовывать данный совет можно двумя путями. По программе на
рис. 1.6 машина запрашивает N элементов списка, а затем (см. строки 80 и 90)
сообщает пользователю, чему равны минимальное и максимальное значения во
введенном списке.
Рис.1.6. BASIC-программа ввода элементов массива с фиксацией
диапазона изменения
Если при этом, например, пользователь вводил числа с форматом три знака
до запятой и два знака после, то пропуск десятичной точки сразу скажется на
значении максимального числа в списке.
Если же пользователь заранее знает хотя бы примерный диапазон
изменения значений элементов списка, то советуем ему поделиться такой информацией
с машиной до ввода списка, как это предусмотрено в программе на рис. 1.7.
Рис.1.7. BASIC-программа ввода заанее обозначенного массива
В этой программе заложен
так называемый принцип "защиты от дурака". Мы к нему еще часто будем
возвращаться на страницах книги. Цикл "до" (строки 50 и 60 на рис.
1.7) выполняет функции контрольно-пропускного пункта и не позволяет ввести
заведомо неверные данные.
Не балуй машину! Не делай за нее то, что она может прекрасно
сделать и без тебя.
Эту суть можно отнести ко многим советам книги, но здесь она видна
наиболее ясно.
Если числа в списке имеют одинаковый формат: 2.5, 4.8, 9,3, 0.2 и
так далее (об этом мы упомянули уже в предыдущем совете), то можно существенно
упростить и ускорить их ввод в ЭВМ. Ведь во вводимых числах одна половина
информации переменная (две цифры), а вторая (десятичная точка и символ
окончания ввода) – постоянная.
Вот эту постоянную часть работы и стоит переложить на плечи машины, что и
сделано в программе на рис. 1.8.
Рис.1.8. BASIC-программа ввода массива с известным фоматом
элементов
Для этого пришлось
отказаться от оператора INPUT и задействовать операторы опроса клавиатуры без
прерывания (строки 60
– ввод
единиц и 90 – ввод
десятых частей) и операторы перевода литерной переменной в числовую (строки 80
и 110). Остальное (ввод десятичной точки и нажатие клавиши "Ввод")
машина сделает сама. Отказ от оператора INPUT имеет еще одно преимущество – на клавиатуре блокируются
все клавиши, кроме цифровых, нужных в данный момент (строки 70 и 100).
Алгоритм ввода десятичных чисел, реализованный программой на рис.
1.8, был, кстати, в ходу на первых версиях Фортрана, где запрос значения
переменной оговаривался определенным форматом.
Непронумерованный совет. Помни! Очень часто новое – это хорошо забытое старое.
Ранние версии языков программирования можно не только поругивать. Иногда стоит
из них выуживать что-то ценное.
Исключить десятичную точку во вводимых числах можно и не
отказываясь от оператора INPUT, а введя какой-то коэффициент
пересчета: 9,8 = 98 . 0,1, например.
Многие люди не привыкли к англоязычной манере отделения точкой
десятичной части числа от целой и продолжают использовать для этого запятую.
Если программист не хочет по каким-то причинам ломать привычек пользователей
программы, то ему можно посоветовать записывать число оператором LINPUT в
литерную переменную, а затем переводить ее в числовую. Запятая при этом
превратится в точку, что удовлетворит и человека, и машину (непронумерованный
совет!).
Давай пользователю оперативную возможность исправления ошибок.
Человек вводит список в ЭВМ, заполняя не только память машины, но и
верхнюю часть дисплея, что дает возможность исправить допущенную ошибку. Если
она замечена в очередном элементе до нажатия клавиши "Ввод", то
исправить ее не составит особого труда. Для этого на клавиатуре есть
специальные клавиши перемещения курсора, вставки или исключения символов и др.
Мягкая копия (текст на дисплее) отличается от твердой (текст на бумаге) в
первую очередь тем, что к мягкой копии не относится пословица "что
написано пером, не вырубишь топором".
Рис.1.9. BASIC-программа ввода элементов массива с возможностью
исправления
Программа, помещенная на рис. 1.9, позволяет исправлять допущенные
ошибки и в ранее введенных элементах списка. Если замечена ошибка, то нужно
вместо очередного числа ввести нуль (строки 40 и 50 на рис. 1.9). Машина после
этого запросит номер элемента с ошибкой и предоставит возможность исправить ее
(строки 80-100). Ввод нуля при запросе номера элемента с ошибкой возвращает к
вводу элемента списка, на котором работа была прервана (первый оператор строки
70). Будущие ошибки, конечно, исправлять нельзя (конец строки 70).
Исправление ошибок ведется с учетом того факта, что человек – существо далеко не
совершенное, которое может, исправляя старые ошибки, наделать новых. Машина
просит человека не набирать по-новому число без ошибки, и предоставляет ему
возможность сделать исправления в старом. Вот почему число с ошибкой в
программе на рис. 1.9 сначала переводится в литерную форму (строку 90),
исправляется (начало строки 100), а затем обратно переводится в числовую (см.
второй оператор строки 100).
Непронумерованный совет. Требуя от пользователя ввода списка,
подробно растолкуй ему, как это делать, – взгляни на строки 20 и 30 на рис. 1.8 и на строку 20 на рис. 1.9.
Непронумерованный совет( за который нас поблагодарят медики).
Подводя черту под советами, связанными с вводом списка, нельзя не
упомянуть вот о чем. Во многих ПВЭМ заложена возможность изменения размеров
символов на экране дисплея. Не забывай о ней, помни, что разглядывание мелких
знаков на дисплее, тем более если в качестве его используется телевизор, влияет
на зрение отнюдь не лучшим образом. Для лиц с плохим зрением можно даже
смоделировать цифры и буквы во весь экран дисплея.
Приучай пользователя твоей программы к некоторым условным
символам в общении с машиной.
Очень часто в диалоге с человеком машина задает ему вопрос, ответ
на который несет один бит информации: "Включен ли дисковод?",
"Заправлен ли принтер бумагой?", "Не разлюбил ли ты меня?"
и т.д. Есть много форм ответа на подобные (их называют альтернативными)
вопросы, вплоть до "Молчание – знак согласия". В принципе можно научить машину понимать все
из них, но удобнее договориться с ней заранее, что, например, нажатие клавиши
"1" означает "Да", а "0" – "Нет", как это
сделано в программе (рис. 1.10, авторы И. и П. Мордкович), реализующей на ЭВМ
игру
Рис.1.10. BASIC-программа "Животные": битовый запрос без
прерывания
"Животные" [37]. Правила и ход этой игры лучше пояснить
примером
ее протокола:
ЭТО КОТ? 0
СДАЮСЬ, КТО ЭТО? КИТ
ЧЕМ КИТ ОТЛИЧАЕТСЯ ОТ КОТ? ОН ЖИВЕТ В ВОДЕ
БУДЕТЕ ЕЩЕ ИГРАТЬ? 1
ОН ЖИВЕТ В ВОДЕ? 0
ЭТО КОТ? 0
СДАЮСЬ, КТО ЭТО? СТУДЕНТ
ЧЕМ СТУДЕНТ ОТЛИЧАЕТСЯ ОТ КОТ? ОН ХОДИТ НА ЛЕКЦИИ
БУДЕТЕ ЕЩЕ ИГРАТЬ? 1
ОН ЖИВЕТ В ВОДЕ? 0
ОН ХОДИТ НА ЛЕКЦИИ? 1
ЭТО СТУДЕНТ? 0
СДАЮСЬ, КТО ЭТО? ПРОФЕССОР
...... и т.д. до бесконечности.
Согласно этой игре человек задумывает животное (предмет, явление),
которое машина должна отгадать, задавая альтернативные вопросы, разбивающие все
множество задумок человека на две части (принцип бинарного поиска – см. программы на рис. 0.8).
Машина как бы взбирается на двоичное дерево, ветви которого (альтернативные
вопросы) при ответе человека (да – нет) раздваиваются. На ветвях "висят" ответы на вопросы.
Игра самообучающаяся, т.е. машина сама "выращивает" дерево вопросов и
ответов, которое хранится в двух литерных массивах В (вопросы) и 0 (ответы) и в
одном числовом Р (логические переходы, ведущие к ответу).
Если нового уточняющего вопроса у машины не окажется (т.е. пуст
элемент массива В – см. строку
40 на рис. 1.10), то машина выдаст свою догадку (строка 60). Если она окажется
неудачной, то (см. левое плечо альтернативы на строках 70-100) машина запросит
задуманное человеком (строка 80) и попросит сформулировать новый альтернативный
вопрос, содержащий признаки отличия задуманного от догадки. Так ЭВМ учится на
своих ошибках.
Человек в игре "Животные" должен честно отвечать
"да" или "нет" на альтернативные вопросы машины.
Поддерживает такой диалог подпрограмма с именем "1", хранящаяся на
строках 130-160. Она подсказывает форму ответа (см. строку 130) и требует
нажатия клавиш "1" или "0", блокируя остальные. Оператор
ввода информации без прерывания (строка 140) отличается от его аналога с
прерыванием (строка 80) еще и тем, что оператор без прерывания не пропечатывает
на экране дисплея вводимый символ. Поэтому не забывай это сделать в программе
(см. второй оператор строки 160). Так будет получаться ясный протокол игры (см.
выше), фиксирующий
все ответы человека.
Вопрос читателю. В конце вышеприведенного протокола игры стоит
"и т.д. до бесконечности". Но такого, конечно, быть не может. Что,
помимо отказа человека играть дальше, может ограничить игру?
Ответ ниже, в следующем совете.
Суть совета противоположна той, какая сформулирована в совете 16:
Не требуй от пользователя каких-то условных символов при ответе – сделай машину более гибкой и
понятливой.
В предисловии мы предупреждали читателей, что будем давать
взаимоисключающие советы. Мы считаем это нормальным – читатель, спроси у друзей,
как тебе поступить в трудной ситуации, и ты получишь массу советов, многие из
которых будут исключать друг друга.
Этот совет мы разберем на анализе недостатков программы на рис.
1.10, помещенной в предыдущем совете.
Человек, узрев на экране дисплея альтернативный вопрос, скорее
всего потянется не к клавише "1" (Да) и не к клавише "0"
(Нет), а к другим: "Д", "Y" (Yes), "J" (Ja),
"Н", "N" (No, Nein) и т.д. Не нужно пресекать эти движения,
как предусмотрено программой на рис. 1.10, требующей строго детерминированного
ответа. Программа на рис. 1.11, реализующая игру "Животные",
реагирует на нажатие одной из трех клавиш "Д", "Y" и
"1" при ответе "Да" и одной из трех клавиш "Н",
"N" и "0" при ответе "Нет" на любых
(русский-латинский, строчной-прописной) регистрах (см. строки 16 и 18 на рис.
1.11).
Рис.1.11. BASIC-программа "Животные 1": естественный
ответ на битовый запрос
Непронумерованный совет. Если в твоей машине предусмотрена
программная возможность смены регистров, то перед запросом информации от
человека устанавливай нужный в данный момент регистр, чтобы у человека об этом,
как говорится, "не болела голова".
Задание читателю. Переделай программу на рис. 1.11 так, чтобы
ответ человека "Да" воспринимался машиной по принципу "Молчание – знак согласия". Ответу
"Нет" пусть соответствует нажатие любой клавиши ранее окончания
обусловленного промежутка времени после появления вопроса.
Второе, более существенное отличие программы на рис. 1.11 от
программы на рис. 1.10 составляет суть ответа на вопрос, заданный читателю в
предыдущем совете.
При игре по программе, помещенной на рис. 1.10, дерево ответов
хранится массивами в оперативной памяти машины, что, во-первых, ограничивает
число вопросов за один тур игры до девяти (29 = 512+1 – см. строку 10 на рис. 1.10),
а во-вторых, приводит к потере дерева ответов и вопросов при отключении машины
или переходе к работе по другой программе. В новой игре это дерево приходится
выращивать заново.
Дерево вопросов и ответов для игры, реализованной программой на
рис. 1.11, хранится в архивной памяти машины (на магнитном диске) в виде файла,
имя которого – литерное
выражение номера ветви. Ветви же нумеруются подряд, начиная с корня (00000001),
и далее по его ярусам слева направо: первый ярус – 2 и 3, второй
– 4,
5, 6 и 7, третий – 8, 9, 10,
11, 12, 13, 14 и 15 и т.д. с удвоением (см. начало строки 4 на рис. 1.11) числа
веток на ярусах. Признаком исчерпания вопросов является не пустой элемент
массива (см. строку 40 на рис. 1.10), а отсутствие файла на диске (см. попытку
его открытия на строке 5 на рис. 1.11). Последний оператор строки 1 программы
на рис. 1.11 улавливает такую аварийную ситуацию, но не прерывает работу
программы, а передает управление на строку 10, где, во-первых проверяется
честность игрока (см. строки 11 и 12), а во-вторых, формируется и записывается
на диск новый вопрос (см. строку 13).
Непронумерованный совет. Вводя в ЭВМ программу, записывай в ней
полностью комментарии, текстовые сообщения. Не откладывай эту работу на потом – этого "потом", как
правило, не бывает: лень, недосуг, нетребовательность заказчика и др.
Не оговаривай форму ответа человека какими-то условиями.
В программах, реализующих игру "Животные" (см. рис. 1.10
и 1.11), мы оговорили форму ответа на альтернативный вопрос, но не обусловили
ответ человека на запрос машины дать новый альтернативный вопрос. Если же
взглянуть еще раз на протокол игры, помещенный в совете 16, то можно заметить,
что человек давал альтернативный вопрос без вопросительного знака. Машина же
выдавала его затем со знаком вопроса. Это следствие работы оператора INPUT (см.
конец строки 90 на рис. 1.10 и второй оператор строки 13 на рис. 1.11). Но
естественно ожидать, что человек замкнет свой вопрос соответствующим знаком. В
таком случае машине ничего не останется, как замкнуть свою фразу двумя знаками
вопроса. А это нехорошо. В этой ситуации можно, конечно, попросить человека не
ставить знака вопроса, но ... взгляни выше на суть совета и поступи по-другому
так, как это сделано в программе, помещенной на рис. 1.12.
Рис.1.12. Фрагмент BASIC-программы обработки вопроса
Человек может поставить в конце своего вопроса, запрошенного
строкой 10 (рис. 1.12), сколь угодно вопросов и других знаков, код которых
меньше кода латинской заглавной буквы A. Машина эти знаки безжалостно выкинет
(цикл "пока" на строках 20 и 30) и выдаст вопрос в чистом виде
(строка 40), оканчивающийся буквой и знаком вопроса.
Совет 19
Звуковой сигнал перед печатью
Рис.V
Если тебе приходится просматривать на дисплее периодически
появляющуюся информацию, то сделай так, чтобы машина оглашала звуковым сигналом
или мелодией ее очередное появление. Это позволит в паузах спокойно заниматься
другим делом без нервного поглядывания на дисплей.
Весь совет, как мы видим, уместился в одной фразе. Но, тем не
менее, приведем программу (рис. 1.13), его иллюстрирующую.
Рис.1.13. BASIC-программа "Часы"
Наш совет имеет непосредственную реализацию в повседневной жизни – это часы с боем. Они сами в
нужный момент дадут знать, что на них нужно взглянуть. Программа на рис. 1.13
имитирует такие часы, опираясь на значение системной литерной переменной TIME.
Пока средние два байта этой переменной, хранящие минуты, больше нуля (строка
3), часы молча показывают текущее время (строка 2). Когда же минутная стрелка
сравняется с двенадцатью, то наши часы сначала определят, сколько раз им бить
(строка 4), а потом пробьют (строка 7). Часы имеют "вечный" завод
(строка 9).
Непронумерованный совет. Выбирая себе в магазине персональный
компьютер (не у нас, конечно, а там – за тридевять земель), позаботься, чтобы он был с батарейкой, не
допускающей остановки встроенных часов после отключения компьютера. Это избавит
тебя от необходимости каждый раз выставлять часы после включения ЭВМ (строка 1
на рис. 1.13).
Задание читателю. Доработай программу на рис. 1.13 так, чтобы часы
с боем были и будильником.
Встроенные часы компьютера имеют еще одно применение помимо работы
в программах реального времени. Их можно использовать как генератор случайных
чисел (см. рис. 7.3). Если нужно, например, напечтать случайное целое число в
диапазоне от 0 до 59, то поможет оператор
? RIGHT (TIME , 2)
Так, кстати, многие поступают и в реальной жизни, решая задачу
Буриданова осла и отыскивая ответ, глядя на электронные часы и проверяя – четное или нечетное число
минут в данный момент.
Пользуйся двумя способами задержки вывода информации на
дисплей.
В то время как ученые бьются над проблемой повышения быстродействия
работы компьютеров, программисты то тут, то там вставляют в свои программы
операторы задержки вывода информации на дисплей. Дело в том, что есть задачи,
решаемые человеком и машиной в "одной упряжке", и операторы задержки
синхронизируют их работу. Более того, опытные разработчики компьютерных систем
давно подметили, что некоторых пользователей раздражает молниеносность ответов
машины. Задержка ответа в этом случае имитирует "глубокомыслие"
машины.
Придержать же новую фразу машины можно либо до подачи человеком
определенного сигнала, либо заранее оговоренной паузой. Оба способа имеют как
плюсы, так и минусы. Читатель может их оценить, пообщавшись с компьютером,
работающим по программе (рис. 1.14), имитирующей ведущего в игре
"Лото".
Рис.1.14. BASIC-программа "Лото": два вида задержки
Ведущий этой игры
вытаскивает фишки из мешка и называет целые неповторяющиеся числа в диапазоне
от 1 до 90 (первый оператор строки 30). Массив В (числовой по форме и
логический по содержанию) обеспечивает неповторяемость случайных чисел (цикл
"пока" на строке 30).
Если компьютер не придержать, то он за один миг высыпет весь мешок
на стол играющим. А паузы между выкриками номеров фишек в этой игре необходимы:
ведь нужно успеть просмотреть свои и чужие карточки, прокомментировать игру,
сходить попить водички. Для этого в программе предусмотрены операторы задержки
пустым циклом (строка 160) или с разрешением на вытаскивание новой фишки по
сигналу человека (строка 170), который должен для этого нажать любую клавишу, кроме
пробела. Такую работу можно поручить даже старой бабушке – вот почему на строке 170
стоит оператор опроса клавиатуры без прерывания. Оператор INPUT для бабушки был
бы несколько сложноват, так как он требует нажатия минимум двух клавиш,
последняя из которых "Ввод".
Операторы на строке 160 подобны путам, какие надевают на ноги
пасущейся лошади, чтобы она далеко не ушла. Операторы же строки 170 можно
сравнить с вожжами.
Непронумерованный совет. Программируя работу компьютера в
качестве игрового партнера, не забывай о специфических словах и
выражениях игры
– см.
строки 50-140 на рис. 1.14. Все это будет
"очеловечивать" компьютер.
Заблокировать клавиши клавиатуры можно не только программными (см.
советы 14, 16 и 17), но и аппаратными средствами.
К аппаратным средствам часто прибегают кассиры в магазинах, когда
накрывают колпачками кнопки крупных купюр, чтобы не выбить по ошибке десять
рублей вместо десяти копеек, например.
На клавиатуре ЭВМ тоже есть опасные клавиши, нажатие на которые
грозит крупными неприятностями. Так, на популярной ПК "Искра 226"
клавиша LIST (распечатка текста) и клавиша CLEAR (стирание программы и данных)
расположены рядом. Многие программисты клавишу CLEAR выламывают совсем от греха
подальше, предпочитая слово CLEAR набирать побуквенно.
На ПК IВМ РС (наши "кальки" ЕС 1840, "Искра
1030" и др.) "опасную" в плане сохранности программы или
прерывания работы по ней клавишу можно нажать, нажав одновременно две или даже
три клавиши клавиатуры. Это исключает прерывание работы по программе даже в том
случае, если случайно облокотиться на клавиатуру.
Не всегда дублируй нажатие клавиши клавиатуры печатью
соответствующего символа на экране дисплея.
Нередко информация на магнитном диске (или ленте) бывает такого
рода, что ее приходится хранить "за семью печатями". Для этого,
во-первых, сам диск запирают в сейф, во-вторых, включить ЭВМ, с этим диском
работающую, можно только имея специальный ключ к ее "замку зажигания",
а в-третьих, компьютер перед считыванием информации с диска и выдачей ее на
дисплей или печать может запросить у человека пароль, известный только
избранным.
Короче говоря, способы защиты информации от посторонних глаз могут
быть аппаратными (сейф, ключ к замку) и программными (пароль). Ниже будет дано
несколько советов по программной защите данных.
Наиболее распространенный способ допуска к данным – это запрос машиной пароля
или кода, вводимого через клавиатуру. Набираемые символы на экране дисплея при
этом, естественно, не дублируются.
Рис.1.15. BASIC-программа "Пароль"
Программа на рис. 1.15 реализует такой диалог:
- Пароль?
– спросила
машина.
- Семнадцать,
– ответил
человек.
Строки 70-90 программы на рис. 1.15 подскажут читателю, как можно
избавиться от полной альтернативы (см. совет 0D) при выводе комментария с
приставкой "НЕ".
Расширением программы запроса пароля (см. рис. 1.15) является
программа проверки знания таблицы умножения (рис. 1.16).
Рис.VII
Рис.1.16. BASIC-программа "Таблица умножения"
Паролем при этом является вопрос машины типа 7.8? (см. строку 5),
а отзывом – ответ
человека. Машина при этом наказывает не только за неверные ответы, сообщая
верный (см. правое плечо альтернативы на строках 9.1 – 10.1), но и за превышение
оговоренного времени (см. правое плечо альтернативы на строке 10.1). Оценки за
ответы выставляются по пятибальной системе – см. множественное ветвление на строках 11.2 – 15.
Задание читателю. Отгадай, что за число 168 записано на строке
10.1 и что за странные номера имеет программа на рис. 1.16. Ответы – в советах 7С и 66
соответственно.
Пароль, запрашиваемый машиной, можно еще больше усложнить,
оговорив не только его символы, но и временные интервалы при их вводе в ЭВМ
через клавиатуру. Пояснить такой сверхтайный ввод информации можно на примере
компьютерного фокуса, придуманного В. Пинаевым из г. Рыбинска Ярославской
области.
Рис.VII
Человек, сидящий у клавиатуры компьютера, просит зрителей задумать
и назвать любое число. ЭВМ это число должна отгадать, выдавая на дисплей свои
предположения.
Зрители должны, глядя на эти предположения машины, называть любые
слова, которые фокусник вводит в ЭВМ через клавиатуру, утверждая, что несмотря
на их совершенно случайный характер они содержат информацию о том, слева или
справа на числовом ряду находится задуманное число от последнего предположения
машины. Располагая такой информацией и руководствуясь принципом бинарного
поиска (деление интервала неопределенности пополам), машина быстро находит
ответ. Секрет фокуса: человек, набирая на клавиатуре ЭВМ очередное названное
людьми случайное слово, вводит в машину фактически лишь один бит информации.
Машина, вернее программа, в нее заложенная, – подсадка, цирковой "рыжий", а фокусник общается с компьютером,
как дрессировщик с собачкой, "знающей" арифметику. При наборе
очередного слова нужно, например, выдерживать различные паузы между вводом
первых двух и последних двух символов.
Данный фокус особое впечатление производит на начинающих
программистов, знающих азы Бейсика, но не подозревающих, что есть и другие
способы ввода информации через клавиатуру помимо оператора INPUT.
Рис.1.17. BASIC-программа "Угадай число 1": ввод тайного
бита
Программа (рис. 1.17) реализует фокус. Ввод очередного слова,
названного зрителями, ведется в теле цикла "до" (строки 50-110)
операторами приема байта с клавиатуры без прерывания. Ввод слова, как и при
использовании оператора INPUT развершается нажатием клавиши "Ввод"
(код этого символа 85
– строка
110). При вводе цепочки фиксируется длительность паузы между вводом первых двух
символов – Р1
(строка 90) и последних двух символов – Р2 (строка 120). Альтернатива, реализованная операторами строк
130-150, сужает диапазон поиска.
Непронумерованный совет. Помни! Персональный компьютер можно в
некотором смысле дрессировать,
– учить
его понимать тайные сигналы пользователя.
При работе с магнитофоном как с архивной памятью вставляй в программу
операторы печати, приказывающие пользователю нажать ту или иную кнопку
магнитофона.
Рис.1.18. BASIC-программа "База данных на магнитной
ленте"
Программа на рис. 1.18 позволяет превратить компьютер в записную
книжку с удобными средствами поиска, расширения и исправления. Эта программа – единственная в книге,
которая в качестве архивной памяти использует бытовой магнитофон. При работе по
другим программам архивной памятью служит дисковод, который от магнитофона взял
возможность записи и перезаписи, а от проигрывателя – оперативность поиска
информации. У магнитофона же есть только одно преимущество – дешевизна и доступность. Но
если дисковод сам без посторонней помощи может найти информацию на диске, то
магнитофон в ней нуждается. Поэтому в программу на рис. 1.18 внесены операторы
печати, побуждающие пользователя, во-первых, к включению магнитофона для
переноса данных с магнитной ленты в оперативную память машины (строка 2) и,
во-вторых, к перемотке ленты и включению записи для обратного переноса измененных
данных из ОЗУ на магнитную ленту (строка 4).
Рис.IX
Из меню, записанного на строке 3, можно узнать, какие услуги
предоставляются при работе с электронной записной книжкой.
Поиск информации в книжке ведется по двум ключевым словам,
связанным логическими соотношениями И, ИЛИ и НЕ (строка 8). Алгоритм поиска
ключевого слова в строке описан в совете 3F.
Логическая переменная R теряет свое нулевое значение, когда
информация в ОЗУ машины становится отличной от информации на магнитной ленте
после выполнения пунктов 1, 4 и 5 меню. Это служит сигналом к перезаписи – см. альтернативу на строке
4.
Непронумерованный совет. Используй на магнитофоне компьютера
кассеты с малым запасом ленты (5-, 10-минутные). Это повышает оперативность
поиска, снижает вероятность обрыва ленты при перемотке.
В программы необходимо вносить участки защиты не только от
"дураков", но и от шутников.
Программируя диалог человека с компьютером, помни, что есть особый
род шутников, получающих удовольствие от попыток розыгрыша ЭВМ. "Много ты,
машина, о себе воображаешь. Посмотрим-ка, проглотишь ли ты вот это?!" – так или примерно так думают
они, вводя в ЭВМ заведомо ложную, сбивающую с толку информацию. Машина должна
уметь ограждать себя от ошибок человека, будь они "вольныя или
невольныя".
Взгляни на программу, помещенную на рис. 1.19.
Рис.1.19. BASIC-программа "Температура в комнате":
защита от шутника
ЭВМ, работая по ней,
защищает программу от неверных данных, а свои электронные внутренности – от перегрева или
переохлаждения (строка 80). В программе собран (строки 50-70) запас реплик,
которые машина может отпускать, если человек ненароком или специально ошибется.
Непронумерованный совет. Иногда стоит слегка пожурить пользователя
за допущенные ошибки (см. строку 110 на рис. 1.19). Дружественность в общении
человека и машины не исключает критики.
Напиши лозунг, стоящий в названии совета, и повесь его в
помещении, где ты создаешь программные продукты.
Перейдя от натурального хозяйства (написание программ для себя) к
товарному производству (разработка программных средств, ну если не на продажу,
то по крайней мере для передачи другим лицам), программисты стали очень большое
внимание уделять вопросам диалога человека с машиной. В этой заканчивающейся
главе мы, конечно, затронули только малую часть проблемы. Последний совет главы
призван как бы нейтрализовать ограниченность нашего разбора проблемы и
подтолкнуть читателя к творчеству.
Написав программу для другого человека, первое время понаблюдай
молча за тем, как ему с ней работается. Отметь характерные ошибки диалогов
"человек-компьютер", "компьютер-периферия",
"человек-периферия". Но не пытайся переучить человека, а внеси в
программу дополнения, изменения, инструкции, исключающие аварийные остановы и
неправильные решения при любом направлении диалога человека с машиной. Эта
работа сложная и, как правило, бесконечная. Зато очень благодарная. Помни!
Клиент всегда (или почти всегда – см. предыдущий совет) прав.
Бейсик (да и другие более-менее уважающие себя языки) давно
обзавелся операторами-анализаторами ошибок. Такой оператор, например,
составляет ядро программы (рис. 1.20) создания на диске небольшой базы данных.
Рис.1.20. BASIC-программа "Энциклопедический словарь":
обработка ошибок пользователя
Программа не оперирует ни
одной числовой переменной, разбор же литерных поможет понять ее алгоритм:
Р – содержание
справки (250 символов), запрашиваемой человеком у машины;
Р1 – дублирование
справки в целях фиксации изменений, сделанных в ней человеком;
С – ключевое
слово поиска;
Е – код
ошибки, возникающей при работе по программе;
N – номер
строки, где ошибка произошла.
Последний оператор строки 1 включает анализатор ошибок. Если
таковая в программе случится, то аварийного останова не произойдет. Управление
программой будет передано строке 8, начиная с которой в программе записаны
операторы обработки ошибок. Человек может не включить дисковод, не вставить в
него диск, не закрыть карман дисковода, не снять защиту от записи – все это не будет служить
причиной прерывания работы по программе. Машина попросит человека (строки 11 и
12) сделать нужные действия и повторит оператор, где произошел сбой.
Алгоритм работы программы простой – ключевое слово является именем файла на диске, хранящем нужную
информацию. Если такого файла пока нет, то попытка его открытия на строке 3
приведет к программной ошибке с номером 73. Эту ситуацию обработает срока 8 и
направит ход на строку 6, согласно которой человек должен поискать информацию в
других источниках и поделиться ею с машиной. Если информация и там не найдена,
то переменная Р остается пустой и запрашивается новое ключевое слово (строка
2). Найденная информация записывается на диск R (строка 7) файлом, занимающим
два (указано в скобках) сектора, с именем, совпадающим с ключевым словом С. При
записи файл сначала открывается (первый оператор строки 7), а затем закрывается
(последний оператор строки 7). В полученную от машины информацию можно внести
исправления (см. оператор LINPUT на строке 4). При этом переменные Р и Р1
становятся неравными, что (конец строки 4) служит сигналом к перезаписи файла
на диске: старому файлу присваивается статус ликвидированного (первый оператор
строки 5), а на его месте пишется новая информация (второй оператор строки 5).
Непронумерованный совет. Не забывай вставлять в программу оператор
выключения анализатора ошибок. Иначе это может привести к курьезам при работе
по новым программам: при ошибке управление на них будет передаваться на самые
неожиданные места программы.
Программа на рис. 1.20 имеет еще одну интересную особенность,
напоминающую о том, что в мире нет полного совершенства. Эта программа написана
для ПК WANG-2200 (наша калька
– "Искра
226"). Бейсик-транслятор этой машины можно считать одним из самых сложных
и совершенных. Но, тем не менее, и у него есть изъяны. Если читатель захочет
развить программу на рис. 1.20, он должен будет сначала ввести ее в ЭВМ, а
потом перенумеровать строки оператором RENUMER. Вот тут-то и будет его ожидать
неприятность. Оператор RENUMER припишет всем номерам строк по нулю, оставив без
изменения литерные константы на строках 8, 9 и 12, что приведет к порче
программы – она не
будет уже работать правильно. "И на старуху бывает проруха", – говорят в таких случаях.
Непронумерованный
совет. Помни! Далеко не боги, а простые смертные системные программисты
составляют трансляторы языков. Если ты узрел в них изъян или недостаток – дай
знать об этом разработчикам.
Проблема оптимизации программ (повышения ее быстродействия и
точности, снижения емкости требуемой памяти и др.) находится как бы в
динамическом равновесии с окружающей средой. С одной стороны, быстродействие,
емкость памяти ЭВМ растут, появляются новые, так называемые оптимизирующие
трансляторы. Это в какой-то мере снижает остроту проблемы оптимизации программ.
С другой стороны, сложность задач, решаемых на компьютере, все время растет.
Это требует не ослаблять внимания к методам оптимизации, которым посвящена
данная глава книги.
Быстродействие и ясность программы – вещи, как правило, взаимосвязанные, поэтому подумай, прежде чем
оптимизировать программу.
На рис. 3.1 представлены два варианта программы поиска корня
алгебраического уравнения методом секущих (см. также совет 00, рис. 0.1). На
строках 40 на рис. 3.1 запрограммирована итерационная формула расчета
Рис. 3.1. BASIC-программы поиска корня алгебраического уравнения
методом секущих:
а) "понятный" вариант
б) "быстрый" вариант
На строке 40 на рис. 3.1, а эта формула записана почти без
изменений, но используя ее, компьютер будет делать ненужную работу: три раза
вычислять значение анализируемой (см. строку 10) функции. Это не страшно, если
функция простая. А если расчет ее значений длится часы?! Алгоритм же решения
задачи требует, чтобы значение функции было рассчитано два раза при первой
итерации и по одному разу при последующих. Это и реализовано в программе на
рис. 3.1, б, ценой более высокого быстродействия которой стала некоторая потеря
ясности. Не избежали мы и еще одной потери: в программе на рис. 3.1, а четыре
переменные (Х1, Х2, Х3 и Е), а в программе на рис. 3.1, б – уже шесть (Х1, Х2, Х3, Е, Y1
и Y2). Но этому можно не удивляться, если еще раз взглянуть на название совета.
Если программисту удается оптимизировать все основные параметры
программы (быстродействие, емкость памяти, точность, ясность, удобство диалога
и др.), то это свидетельствует либо о первоначальном грубом промахе, либо о
высоком искусстве программирования. Кстати, если формула, вместо которой на
строке 10 на рис. 3.1 стоит многоточие, простая, то вариант рис. 3.1, б будет
более медленный, чем вариант рис. 3.1, а, – пересылка переменных тоже занимает время.
Не делай многократно то, что можно сделать всего раз.
Метод оптимизации, проиллюстрированный программой на рис. 3.1,
может показаться крохоборством, если не принять во внимание тот факт, что
программа поиска корня уравнения может быть подпрограммой решения более сложной
задачи и может быть запрятана в теле глубокого цикла. В этом случае
микросекунды потерь выльются в часы. Такие циклы нужно чистить. Эта операция
настолько естественна, что авторы ее уже не раз применяли, особо не
останавливаясь на этом: см. строку 80 на рис. 0.14, обрати внимание на
переменную S в программе на рис. 0.36 и т.д. Проблема чистки циклов особо
актуальна при работе с интерпретаторами.
Вернемся к программе ввода числового массива с фиксацией диапазона
разброса элементов на рис. 1.6. Цикл с параметром этой программы
"утяжелен" операциями задания минимального и максимального значений
(см. строку 40). Но это можно сделать и вне цикла, предположив, что диапазон
разброса во вводимом массиве равен машинному представлению вещественных чисел.
Это и сделано в программе на рис. 3.2.
Рис. 3.2. BASIC-программа ввода массива с фиксацией минимального и
максимального элементов: пример чистки цикла
Непронумерованный совет. Убирай из тела глубокого цикла все
комментарии – интерпретатору,
чтобы понять, что этот участок с ремаркой его не касается, тоже нужно время.
Более менее опытному программисту понять, что в цикле лишнее, а
что нет, не составляет особого труда. Кто-то догадался и обучил этому
транслятор. Так появились оптимизирующие трансляторы. Но не расхолаживайся,
читатель, – такой
транслятор может не уместиться в твоей машине, да и стоит он больших денег.
Поэтому читай эту главу дальше.
Вкладывай циклы так, чтобы они выполнялись за минимальное время.
Чтобы не быть голословными, мы теперь некоторые программы будем
дополнять протоколом с хронометражем прогонки.
На рис. 3.3 помещена программа с пустым двойным циклом. Хоть цикл
и пустой, но на его выполнение все равно тратится время, связанное с
инициализацией циклов. Программа на рис. 3.3, а выполняется почти в два раза
быстрее программы на рис. 3.3, б за счет транспонирования матрицы: внешний
цикл, отсчитывающий строки матрицы, должен быть с меньшим числом шагов, чем
внутренний цикл, отсчитывающий столбцы матрицы. На рис. 3.3, а внутренний цикл
инициализируется три раза, а на рис. 3.3, б, когда матрица поставлена "на
попа", – 1000 раз.
Вот причина такой существенной экономии времени.
В немецком языке, откуда к нам пришло слово "бутерброд",
самого этого слова нет. Немцы говорят: Brotchen mit Butter (булочка с маслом),
Brotchen mit Wurst (булочка с колбасой) и т.д. Есть у них и Brotchen mit Nix
(булочка с ничем). Так вот, наш цикл с параметром на рис. 3.3 – это цикл mit Nix, вернее,
цикл с циклом mit Nix. Структурное программирование имеет аналогии и в
кулинарном деле.
Задание читателю. Сделай "начинку" программам на рис.
3.3 в виде ремарки.
Индексные переменные, из которых складываются массивы, машина ищет
в своей памяти дольше, чем простые.
Компьютер при обращении к
индексной переменной сначала должен по ее имени найти в своей памяти место, где
начинается соответствующий массив, а потом отсчитать число элементов,
соответствующее заданному индексу. Следовательно, исключение из программы индексных
переменных ускоряет ее работу.
На рис. 3.4 помещена программа поиска минимума одномерной функции
(строка 220) методом Фибоначчи, заимствованная из [2]. Метод Фибоначчи (как и
метод золотого сечения
– см.
рис. 0.33) основан на умелом делении и последующем сужении исходного интервала
неопределенности. При использовании метода Фибоначчи для такого деления
используются так называемые числа Фибоначчи: 1, 1, 2, 3, 5 и т.д., где новое
число равно сумме двух предыдущих (см. рис. 0.38). Эти числа рассчитываются на
строках 110 и 120 программы на рис. 3.4 и заносятся в массив F. Но для
алгоритма оптимизации нужны не все эти числа, а только два последних – см. строку 130.
Следовательно, от массива F можно отказаться, что и сделано в программе на рис.
3.5.
Рис. 3.4. BASIC-программа поиска минимума функции на отрезке
методом Фибоначчи: использование массива чисел Фибоначчи вложенных полных
альтернатив
Рис. 3.5. BASIC-программа поиска минимума функции на отрезке
методом Фибоначчи: использование альтернатив с одним плечом и полных логических
выражений
Внимательный читатель заметит еще три существенных различия в
программах на рис. 3.4 и 3.5. Во-первых, в программе на рис. 3.5 на строках
160-190 наблюдается избыток логических соотношений "больше" и
"меньше". Этот избыток полезен – он, с одной стороны, исключил метки в программе (см. также совет
0D), а с другой стороны, ясно выделил логику деления интервала
неопределенности.
Все это понятно, но... Программа на рис. 3.5 имеет очень
интересную особенность: она совершенно правильно отыскивает минимум (Y=-24,3696
при Х=0,7809) на отрезке (-1;1) у довольно-таки сложной функции, записанной на
строке 220. Но если этой программе "подсунуть" функцию Y=Х**2 и
попросить ее найти минимум на том же отрезке, то машина неверно (!?) решит
такую простейшую задачу. Дело в том, что плечи неполных альтернатив, записанных
на строках 160 и 170, меняют значения булевых выражений их заголовков. Это
приводит к тому, что при очередном выполнении цикла с параметром (строки
140-200) могут выполнятся плечи нескольких альтернатив, а не одной, как
задумано алгоритмом метода Фибоначчи. При замене полной альтернативы на две
неполные нужно уметь гарантировать себя от ошибок и вводить "признак"
(см. программы на рис. 0.32, б, 0.33, б, 0.35 и 0.36), исключающий выполнение
обоих плеч исходной полной альтернативы.
Во-вторых, в программе на рис. 3.4 подпрограмма на строке 220
имеет странное окончание, отличное от привычного RETURN. Вызывается же эта
аномальная подпрограмма традиционным способом – через GOSUB. Это еще один нюанс (помимо массива) программы на рис.
3.4, приводящий к растранжированию памяти, которая будет забиваться адресами
переходов к подпрограмме. Здесь по сути записана рекурсия, когда подпрограмма
вызывает саму себя, слова GOSUB на строках 130 и 150 в программе на рис. 3.4
лучше заменить на GOTO. Такая замена еще раз подчеркивает тот факт, что
оператор GOSUB – это
никакой не оператор вызова подпрограммы (тем более процедуры), а просто
оператор безусловного перехода с запоминанием места, где сделан этот переход.
Процедура появилась только в последних версиях Бейсика – у Турбо- и у Квик-Бейсика.
Третье отличие
– в
форме записи цикла с параметром на строках 120. Такую манеру (см. рис. 3.4)
записи цикла с одним оператором Бейсик перенял у Паскаля. Следует отметить, что
Бейсик очень чутко прислушивается к своим критикам и меняется в лучшую сторону.
Это одна из причин такого обилия диалектов Бейсика, о которые
читатель, наверное, "спотыкается", читая книгу.
Замена возведения в целую степень на произведение ускоряет расчет
не всегда.
Внимательный читатель заметил и четвертое различие в программах на
рис. 3.4 и 3.5. В анализируемой функции на рис. 3.4 (строка 220) показатель
степени высокий или если основание – индексная переменная или выражение. В программе на рис. 3.6 на
строке 90 основание квадрата-выражение, содержащее индексную переменную, что и
определило отказ от произведения.
Рис. 3.6. BASIC-программа "Встроенный магазин": замена
произведения возведением в квадрат
Эта программа позволяет определить в микрорайоне дом, где следует
открыть встроенный магазин. Задачу об отдельно стоящем магазине мы уже решали
по программе на рис. 2.2, недостатком которой является плохая сходимость в
случае, если одна из исходных точек окажется вблизи предполагаемого оптимума.
На такой случай и рассчитана программа на рис. 3.6. По ней точка оптимума
ищется методом прямого перебора кандидатов (см. цикл на строках 60-120).
Подсчет целевой функции (см. цикл на строках 80-100) ведется с учетом весов
точек – числа
жильцов в домах.
Задание читателю. Определи на своем компьютере граничный
показатель степени, до которого выгодно применять произведение, а свыше
которого – возведение
в целую степень.
Перестановка мест слагаемых меняет сумму.
Если ты, читатель, считаешь, что мы тебя обманываем, то вот тебе
программа (рис. 3.7), подтверждающая нашу правоту.
Рис. 3.7. BASIC-программы вычисления суммы знакопеременного ряда:
а) пониженная точность расчета;
б) повышенная точность;
По ней суммируются первые пятьсот членов ряда, на который
раскладывается натуральный логарифм [4, 29]. Под суммой ряда мы понимаем то,
что получается на ЭВМ, имеющей ограниченную точность. В случае рис. 3.7, а
(суммирование, начиная с больших по модулю членов) ошибка обнаружилась в 12-м
знаке после запятой, а в случае рис. 3.7, б (суммирование, начиная с меньших по
модулю членов) – за
пределами точности.
Пример, показанный на рис. 3.7, не совсем удачный, – точности компьютера еле-еле
хватило, чтобы была накоплена ошибка. Но читатель может сам придумать более
подходящие примеры, подтверждающие правило о том, что суммирование ряда всегда
нужно начинать с самых маленьких по модулю членов. Если ряд упорядочен (как в
примере на рис. 3.7), то это сделать просто, начав суммирование с
"хвоста".
Суммирование на ЭВМ имеет еще один "подводный камень".
Если вести его долгое время, то может статься, что сумматор
"насытится" и не будет больше менять своего значения. Это в какой-то
мере отображает закон природы, где всегда суммируются, как правило, величины
одного порядка. У этого правила много исключений. Одно из них – рост массы Земли вследствие
оседания космической пыли. Рассказывают, что экскурсовод палеонтологического
музея называл возраст одного экспоната 20 миллиардов и 5 лет. Когда его
спросили, откуда такая точность, то он ответил, что при поступлении на работу в
музей ему назвали цифру 20 миллиардов, а с тех пор прошло 5 лет.
Непронумерованный совет. Программируя на ЭВМ и задаваясь точностью
расчетов, помни, что машина должна отображать ход реальных процессов.
Программу, помещенную на рис. 3.7, можно оптимизировать не только
по критерию точности расчета, но и по быстродействию, воспользовавшись
предыдущим советом. Ряд, на который раскладывается натуральный логарифм, – знакопеременный. Чередование
плюса и минуса в программе на рис. 3.7 велось путем возведения минус единицы в
целую степень. Замена этой операции альтернативой ускоряет расчет – сравни программы и протоколы
на рис. 3.8, а и б.
Рис.3.8. BASIC-прогрмааы вычисления суммы знакопеременного ряда:
а) перемена знака члена возведением в целую степень;
б) перемена знака члена альтернативой;
Задание читателю. Исключи совсем из программы вычисления
натурального логарифма через сумму ряда операцию возведения в целую степень.
Название этого совета почти повторяет смысл предыдущего.
Но в предыдущем совете речь шла о сумме арифметических величин,
здесь же мы затронем величины логические, которые в ряде случаев могут менять и
сумму, и произведение при их перестановке. Более того, перестановка
сомножителей в логическом умножении может вызвать аварийный останов программы.
Поясним эту особенность программой сортировки массива (рис. 3.9).
Рис. 3.9. BASIC-программа сортировки числового массива:
а) с использованием логического умножения;
б) с использованием двух операторов условного перехода.
Программа на рис. 3.9, а иллюстрирует применение логического умножения
(AND) при сортировке вводимого с клавиатуры числового массива A, когда его
новый элемент сразу после ввода перемещается в массиве на подобающее ему место
(цикл "пока" на рис. 3.9, а). В ранних версиях Бейсика логических
операций не было, и программисты выходили из положения, записывая в программу
несколько операторов условного перехода вместо одного. Так и сделано в
программе на рис. 3.9, б (аналог программы на рис. 3.9, а), где условный
переход на строке 40 реализуется двумя операторами.
Излишняя педантичность логических операций AND и OR может вызвать
сбой, если второе логическое соотношение имеет смысл лишь тогда, когда ложно
первое. Так, использование операции OR в строке 40 программы на рис. 3.9, б
приведет к авосту (так на жаргоне програмистов называется аварийная остановка)
при вводе самого первого элемента массива А, когда J=1. Дело в том, что версия
Бейсика программы на рис. 3.9, а имеет нулевые элементы в массивах, а версия
Бейсика программы на рис. 3.9, б – нет.
Для исключения возможных ошибок избегай проявлений излишней
педантичности логических операций. Помни! Перестановка местами слагаемых в
логических выражениях в отличие от арифметических может исказить результат:
попробуй переставить местами операторы строки 40 в программе на рис. 3.9, б.
Непронумерованный совет. Если есть возможность, то всегда отмечай
в программе, какой минимальный элемент в массиве тебе нужен – см. строку 100 на рис. 3.4.
На рис. 3.10 представлена программа поиска корня алгебраического
уравнения методом Ньютона.
Рис.3.10. BASIC-программа поиска корня алгебраического уравнения
методом Ньютона с оценкой сходимости
От программы, помещенной
на рис. 0.3, она отличается тем, что в нее были добавлены участки контроля
сходимости метода. Цикл "до" на строках 50-80 на рис. 3.10 в своей
логической части имеет два последовательных условных перехода. Объединить их в
один со словом OR нельзя, так как второе условие на строке 80 может
проверяться, только если первое на строке 70 неверно. Иначе произойдет
аварийный останов – деление
на нуль.
Непронумерованный совет. Старайся вести контроль числа итераций в
программах последовательного приближения и прерывай их выполнение, когда
итераций становится слишком много – см. строку 140 на рис. 3.10.
Не увлекайся знаками логических выражений. Такое увлечение чревато
потерей быстродействия помимо тех неприятностей, о которых было сказано в
предыдущем совете.
Заменяй одно логическое выражение со многими знаками операций
несколькими. В обоснованности этого совета тебе помогут убедиться две программы
на рис. 3.11 и 3.12.
Рис.3.11. BASIC-программы поиска наименьшего общего кратного двух
чисел:
а) двойной цикл "до";
б) одинарный цикл "до" с логическим сложением.
Рис.3.12. BASIC-программы решения обыкновенного дифференциального
уравнения методом Рунге-Кутта:
а) с двумя операторами условного перехода;
б) с одним оператором условного перехода.
Первая из них ищет наименьшее общее кратное методом прямого
подбора, а вторая решает обыкновенное дифференциальное уравнение методом
Рунге-Кутта. И в том и в другом случае переход к двум операторам условного
перехода (варианты рис. 3.11, а и 3.12, а) ускоряют расчет.
Заметь, в программу на рис. 3.12 была вставлена отладочная функция
(строка 10), вид которой не меняется и при интегрировании, и при
дифференцировании, т.е. показательная функция с основанием е. Значение ее при
Х=1 легко можно запомнить
– 2,7
плюс два раза Лев Толстой (т.е. год его рождения).
Делай так, чтобы компьютер не простаивал, когда ты изучаешь
информацию на дисплее.
Если в цикле необходимо выводить на дисплей промежуточные
результаты и приостанавливать расчет для их просмотра и анализа, то
соответствующую команду задержки лучше помещать до, а не после операторов
печати. Этим можно существенно ускорить расчет, так как человек и компьютер в
этом случае будут работать параллельно, а не последовательно.
Сущность описанного приема лучше всего пояснить примером.
Рис.XIV.
Рис.3.13. BASIC-программа "Семейный бюджет"
Рис.3.14. Примеры протоколов работы с программой "Семейный
бюджет":
а) новая запись;
б) обработка записей.
На рис. 3.13 помещена программа, превращающая компьютер в семейную
книгу доходов и расходов. Протоколы работы по этой программе, помещенные на
рис. 3.14, поясняют два режима работы: запись расходов семьи, имевших место
накануне (рис. 3.14, а) и обработку записей, хранящихся в архивной памяти рис.
3.14, б). Эти записи хранятся файлами, имя которых отображает дату фиксации расходов.
Так, если на диске есть два смежных файла с именами "87.02.01" и
"87.02.05", то это означает, что второй файл хранит данные о доходах
и расходах в период с 1 по 5 февраля 1987 г. Каждый день записи можно не
делать, иначе компьютер превратится в тирана, требующего ежедневного общения с
ним.
Программа "Семейный бюджет" нуждается в двух
подпрограммах (см. строки 5001 и 5007) обработки календарных дат и формирования
имен файлов на диске. Эти подпрограммы следует заимствовать из программы на
рис. 5.10.
Поиск информации в архивной памяти ведется методом проб и ошибок:
машина перебирает все дни в указанном периоде и выводит на дисплей информацию
по дням, файлы которых есть на диске. Обрати внимание на оператор задержки,
стоящий в конце строки 19. Естественнее было бы поставить его на строке 21
вслед за операторами печати, но в этом случае машина простаивала бы. Если же
задержка организована до операторов печати, то в момент просмотра человеком
предыдущей информации на дисплее машина будет "выуживать" с диска и
обрабатывать новую, выдав ее на дисплей только после разрешения человека.
Так, переставив в программе один-единственный оператор на новое
место, можно существенно ускорить расчет, организовав параллельную работу
человека и машины.
Покупка домашнего компьютера – дело серьезное. Его стоимость лежит где-то между стоимостью
цветного телевизора и мотоцикла с коляской. Доказать полезность компьютера в
домашнем хозяйстве не так просто. Покупать же его, не посоветовавшись с семьей,
опасно. Можно оказаться в ситуации героя рассказа Василия Шукшина, купившего
микроскоп и вступившего в связи с этим в крупный конфликт с супругой.
Два непронумерованных совета.
1) Если ты купил домашний компьютер, то заведи и программу
"Семейный бюджет". Контролируя и регулируя расходы, можно закрыть
брешь в бюджете, вызванную покупной компьютера. Кроме того, взгляни на рис.
3.14, б. В феврале 1987 года семья потратила на развлечения немногим более двух
рублей, что составляет 0,6 % всех расходов. Дело в том, что домашний компьютер,
приобретенный в начале 1987 года, может не только делать дыры в бюджете, но и
латать их, обеспечивая бесплатные развлечения – игры, головоломки, опробование программ этой книги и прочее.
2) Переделай программу "Семейный бюджет" в программу
"Семейная летопись", с помощью которой можно заносить в архивную
память мысли, дела, впечатления прожитого дня. Такая программа, например,
поможет легко парировать такой типичный упрек-восклицание жены: "Я уже не
помню, когда мы были последний раз в театре?!". В такой ситуации нужно
запустить программу "Семейная летопись" и провести ретроспективный
анализ записей с ключевым словом "Театр".
Умело пользуйся буферной памятью принтера.
Распараллелить и тем самым ускорить можно не только работу
человека и компьютера, как отмечено в предыдущем совете, но и компьютера и
принтера. Есть принтеры с буферной памятью, куда компьютер может
"выстрелить" очередной порцией информации и работать дальше, не
дожидаясь, пока неповоротливый принтер ее распечатает. Это обстоятельство
следует учитывать при составлении программ.
Рис.3.15. BASIC-программы поиска простых чисел:
а) для ЭВМ и принтера без буферной памяти;
б) для ЭВМ и принтера с буферной памятью;
в) жертвование быстродействием ради удобства редактирования.
На рис. 3.15 помещены три варианта программы поиска первых десяти
тысяч простых чисел. Заложенный в них алгоритм похож на тот, который был
реализован в программе на рис. 2.6. Только проверка простоты нового числа
ведется последовательным его делением не на предшествующие натуральные числа, а
на ранее найденные простые. ЭВМ эти простые числа должна помнить, поэтому на
строке 10 программы резервируется целочисленный массив.
Программа на рис. 3.15, а предназначена для работы с принтером без
буферной памяти. Поэтому массив Р сначала заполняется простыми числами 2, 3, 5,
7, 11, 13 и т.д. (см. цикл "до" на строках 20-80), а потом
распечатывается на принтере (строка 90).
Рис.XV.
Непронумерованный совет.
Прогнать "длинную" программу на персональном компьютере
коллективного пользования и не вызвать ворчание коллег-совладельцев можно,
оставив машину работать на ночь или даже на выходные, отключив дисплей и всю
периферию. Утром (в понедельник) полученные данные нужно быстро сбросить на
бумагу или на диск.
Для принтера с буферной памятью более подходит программа поиска
простых чисел, помещенная на рис. 3.15, б. В ней оператор печати нового
найденного простого числа стоит в теле цикла (строка 70). Пока принтер будет
это число распечатывать, ЭВМ может найти новое. В начальный момент, когда
простые числа находятся быстро, работу вычислительного комплекса будет
лимитировать принтер –
его
буферная память может переполняться. Затем все будет упираться в быстродействие
компьютера, но суммарное время работы по программе на рис. 3.15, б (счет +
распечатка) будет меньше, чем суммарное время работы по программе на рис. 3.15,
а.
Чтобы изменить общее количество простых чисел, генерируемое
программой на рис. 3.15, а, нужно заменить константу 10 000 в трех местах
программы. а это не совсем удобно. Можно, как это сделано в программе на рис.
3.15, в задействовать переменную К и ей один раз присваивать нужное значение.
Непронумерованный совет.
Заменяя переменные на константы, помни, что это ускоряет счет, но
ухудшает "редактируемость" программ.
Советы 38 и 39 подвели нас к очень важной и интересной теме – параллельному
программированию.
Все операторы программ книги выполняются последовательно. При этом
время выполнения программы равно сумме продолжительностей выполнения каждого
оператора. Есть операторы, находящиеся в причинно-следственной связи, которые
нельзя менять местами (например, строка 20 на рис. 3.15, а). А есть операторы
(три последних на строке 10 на рис. 3.15, а), которые можно переставлять
местами и даже выполнять параллельно. В этом случае суммарная скорость их
выполнения будет определяться скоростью самой медленной.
Работа по повышению эффективности ЭВМ ведется по двум кардинальным
и взаимосвязанным путям: повышения скорости последовательного программирования
и создания средств (ЭВМ типа "Эльбрус", языки типа Ада и др.)
параллельного программирования.
На персональных компьютерах проблема параллельного
программирования решается частично комплектацией периферии (принтеры,
дисководы, графопостроители, звукогенераторы и др.) сопроцессорами и буферной
памятью.
Не гоняй дисковод попусту.
Этот совет поможет устранить один существенный недостаток программы
создания на магнитной ленте базы данных (см. рис. 1.18). Взгляни на нее: все
двести страниц электронной записной книжки перегружаются с ленты в ОЗУ (строка
2) и обратно (строка 4) независимо от того, заполнены они или нет. Такого
недостатка лишена программа на рис. 3.16, где с диска в ОЗУ и обратно
перегружаются только заполненные страницы.
Рис.3.16. BASIC-программа "Локальная база данных"
Это существенно ускоряет работу программы, бережет магнитный слой
диска и головку дисковода от истирания.
Если программу на рис. 1.18 мы сравнили с электронной записной
книжкой, то эту (рис. 3.16) мы уподобим электронной амбарной книге в 500
страниц, на каждой из которых можно поместить информацию длиною до 100 символов
(см. строку 20). Информационный поиск в этой книге ведется по одному или двум
ключевым словам, связанным логическими соотношениями И, ИЛИ и НЕ. При этом
книга открывается на страницах, где есть первое И второе ключевое слово, где
есть первое ИЛИ второе слово (или оба одновременно) и где есть первое слово, но
НЕт второго (см. запрос на строке 120). Если второе ключевое слово не ввести,
то поиск будет вестись только по первому ключевому слову (см. оператор
условного перехода в конце строки 110).
Амбарная книга на магнитном диске хранится в компактном виде – все пустые страницы
находятся в конце книги и нет пустых страниц между двумя заполненными. Поэтому
перегрузка книги с магнитного диска (она там хранится файлом с именем СТРАНИЦЫ)
в ОЗУ машины прерывается, когда встречается пустая страница (см. досрочно
прерываемый цикл с параметром на строке 40). В книге можно сделать новую запись
на первой пустой странице, просмотреть все заполненные страницы или только те,
какие отвечают условиям информационного поиска (см. меню на строке 70).
Просмотр страниц книги ведется с помощью очень удобного оператора LINPUT
(гибрид операторов PRINT и INPUT), что позволяет вносить в записи изменения или
даже совсем стирать их, присваивая элементу массива литерный нуль – цепочку пробелов (см. строку
200).
Перед перезаписью книги на магнитном диске (необходимость в этом
возникает, если в нее были внесены изменения или дополнения и переменная R
потеряла свое первоначальное нулевое значение – см. альтернативу на строках 220-280) она уплотняется – пустые страницы переносятся
в ее конец (см. операторы на строках 230-250). Запись книги на диск прерывается
на пустой странице (см. оператор условного перехода в середине строки 270).
Строка 280 в программе лишняя – оператор END на строке 290, прерывая выполнение программы, закроет
все ранее открытые файлы. Но взгляни в совет 44 и не злоупотребляй принципом
умолчания.
Есть много факторов, влияющих на выбор места подпрограммы в
программе.
Программисты, работающие с Бейсиком, вольны помещать свои подпрограммы
в любом месте программы: в ее голове, в хвосте. Некоторые ухитряются вклинивать
подпрограммы в середину программы. (Таких вольностей, кстати, не позволяет язык
Pascal, но этот язык и появился в первую очередь как средство
"культурного" программирования.)
Но есть правило, регулирующее "прописку" подпрограммы в
Бейсик-программе.
Если подпрограмма несет вспомогательные функции и не меняется от
задачи к задаче (управляет, например, периферией), то ее помещают в конец
программы.
Если же в подпрограмме заключена сущность расчета (записана,
например, анализируемая функция), то ее помещают в заголовке, где ее легко
найти для изменения при переходе к решению новой задачи.
Но если ты поставил своей целью ускорение счета по программе, то
это правило в ряде случаев придется нарушить. Дело здесь в том, что скорость
обращения к подпрограмме зависит от ее местоположения. Подтверждение тому – программы на рис. 3.17 и
3.18.
Рис.3.17. BASIC-программа поиска минимума функции методом
Фибоначчи: подпрограмма в "голове программы"
Рис.3.18. BASIC-программа поиска минимума функции методом
Фибоначчи: подпрограмма в "хвосте" программы
Обе они ищут минимум
одномерной функции методом Фибоначчи (см. также рис. 3.4 и 3.5), но в первой
анализируемая функция записана в заголовке (строка 10), а во второй – в конце (строка 6000). Этим
и вызвана разница в длительностях их прогонки. На других машинах картина может
быть противоположная –
все
зависит от особенностей транслятора языка.
Непронумерованный совет. Размещая подпрограммы в заголовке
программы, не забудь обогнуть их оператором безусловного перехода (см. строку 1
на рис. 3.17). (Заголовок Бейсик-программы, кстати говоря, – понятие чисто условное.)
Если же подпрограмма находится в конце программы, то раздели их оператором
останова (см. конец строки 215 на рис. 3.18).
Правило о размещении подпрограмм можно отнести и к функциям
пользователя, блокам данных и другим конструкциям косвенного использования.
Есть, правда, версии языка BASIC, где функция пользователя, например,
включается в работу только после того, как оператор ее определения будет
выполнен в явном виде. Такую функцию пользователя следует размещать только в
заголовке программы.
"Поколдуй" над программой – и затраченное время вернется тебе сторицей.
Этот совет мы проиллюстрируем задачей, с которой сталкиваются
исследователи при обработке экспериментальных данных однофакторного
эксперимента. Результаты такого опыта, как правило, сводятся в таблицу с Т
парами данных: независимыми переменными и откликами объекта исследования. Эти
точки всегда желательно описать какой-либо аналитической зависимостью. Такую
задачу мы уже решали, описывая точки прямой линией (см. рис. 0.19, 0.20 и 0.29)
и кривой произвольного порядка (см. рис. 0.14 и 2.1).
Но экспериментальные точки могут выписывать более сложные кривые.
В этом случае есть два способа их обработки. Если известен физический закон,
связывающий входной и выходной параметры системы, то в соответствующей формуле
уточняют только значения коэффициентов. Если физический закон неизвестен, то
стараются подобрать аналитическую зависимость (ее вид и ее коэффициенты),
график которой наилучшим способом вписывается в экспериментальные точки. В [19]
дана Бейсик-программа, позволяющая такую работу провести в полуавтоматическом
режиме, когда человек указывает машине формулу, а она подсчитывает ее
коэффициенты. Если формул много, то такая работа будет длиться очень долго.
Программа на рис. 3.19 проводит статистическую обработку экспериментальных
данных в автоматическом режиме: человек сообщает машине число и координаты
точек (строки 16 и 17), а машина "примеривает"к ним 15 аналитических
зависимостей (циклы на строках 18-31) и сообщает вид и коэффициенты наиболее
подходящей(см.строки 32 и33).Вид 15 зависимостей дан на строках 1 – 15 программы.
Рис.3.19. BASIC-программа "Религиозный анализ":
автоматический выбор аппроксимирующего уравнения
Непронумерованный совет. Всегда дополняй оператор включения
индикатора ошибок участком их анализа. Этого не сделано в программе на рис.
3.19, но сделано в программе на рис. 1.20. Мало ли какие другие ошибки возможны
в программе?!
Последний оператор на строке 32 LIST К0 – фантазия авторов. Такого
оператора "не может быть, потому что не может быть никогда" – он находится в противоречии
с оператором произвольной перенумерации строк программы RENUMER.
Непронумерованный совет. Проявляй свою фантазию не только в
составлении программы из готовых операторов, но и в придумывании новых
операторов. Вдруг твоя фантазия окажется не бесплодной и подтолкнет системных
программистов к творчеству!
Доверяй, но проверяй.
Системных программистов, разрабатывающих трансляторы языка, нужно
не только подталкивать к творчеству (см. конец предыдущего совета), но и в ряде
случаев проверять.
Вернемся на минутку к программе на рис. 3.7 вычисления значения
логарифма через сумму ряда. Анализируя ее, мы сделали вывод, что цикл с
параметром должен при суммировании иметь отрицательный шаг, так как в этом
случае значение логарифма, подсчитанное с помощью ряда, совпадает со значением,
выдаваемым встроенной функцией (см. рис. 3.7, б). А почему, собственно, мы
взяли за эталон показание встроенной функции?! Нет ли там ошибки?!
Обычно разработчики трансляторов гарантируют точность в последнем
знаке при принятой на машине форме представления числа. Но это можно легко
проверить, учитывая, что в языке есть прямые и обратные функции (SIN-ARCSIN,
квадрат – квадратный
корень и т.д.). По программе, помещенной на рис. 3.20, ведется такой контроль
экспоненты (по основанию e) и натурального логарифма.
Рис.3.20. BASIC-программа оценки точности встроенных функций
Задание читателю. Оцени точность встроенных функций своего
компьютера.
Непронумерованный совет. Повышать надежность расчетов можно не
только оптимизацией алгоритма, но и выбором транслятора повышенной точности,
ведь точность зависит не только от длины мантиссы вещественных чисел, но и от
качества встроенных функций. Эти функции компьютер, кстати, вычисляет через те
же суммы рядов.
Внедряй на производстве энергосберегающие технологии.
Начнем издалека.
Одна из сфер применения компьютеров – это обучающие тренажеры. Так вот, есть тренажер обучения танкистов
такому ведению боя, при котором не просто будет достигнута победа с малыми
потерями, но и расход горючего при этом будет минимальным. К пословице "Не
до жиру – быть бы
живу" здесь не прислушиваются.
Теперь можно вернуться к сути совета.
При разработке программы можно оптимизировать не только ее
скорость, точность, надежность, но и расход электроэнергии, потребляемой
компьютером при ее прогонке. Мы, правда, с этим советом опоздали лет на
двадцать – современный
ПК потребляет энергии не больше, чем телевизор. Но все же, взгляни на программу
(рис. 3.21) поиска первых десяти тысяч простых чисел. Она получилась после
структурирования программы на рис. 3.15, описанной в совете 39.
Рис.3.21. BASIC-программа поиска простых чисел с операторами
управляющими электроснабжением периферии
Наша новая программа (рис.
3.21) сначала найдет десять тысяч простых чисел, затем включит принтер,
распечатает на нем полученные цифры, выключит его, включит дисковод, откроет на
диске файл с именем DATA, занесет в него числа, напечатанные ранее на бумаге,
закроет файл после записи, выключит дисковод и, наконец, выключит саму машину.
Рис.XVa.
Читатель, не ищи описание операторов включения и выключения ЭВМ и
ее периферии в списке в конце книги. Все это опять же (см. совет 3С) фантазия авторов.
В совете 39 мы упомянули о том, что ЭВМ иногда можно оставить
работать на выходные, согласовав, конечно, этот вопрос с пожарниками.
Так вот, программу на рис. 3.21 можно запустить перед уходом в
отпуск, попросив соседку, которая будет заходить к вам поливать цветы,
посматривать заодно и за домашним компьютером.
Задание читателю. Вспомни или придумай практическую задачу, для
решения которой понадобилось бы знание десяти тысяч простых чисел.
Непронумерованный совет. Не загружай компьютер бестолковой работой.
Это самый лучший способ экономии электроэнергии.
Рис.XVI.
Не спеши решать задачу, подожди – может быть появится язык, на котором она решается оптимальным
образом одним оператором.
Это, конечно, наикардинальнейший путь и решения задачи, и
оптимизации соответствующей программы. Но разумнее поискать языки или их
диалекты, наиболее подходящие для решения твоей задачи. Ведь многообразие
алгоритмических языков вызвано не только развитием программирования, вкусами
программистов, но и сложностью решаемых задач. Кроме того, хороший системный
программист следит за своим детищем и дополняет транслятор операторами, в
которых нуждается пользователь.
Рис.3.22. BASIC-программы поиска в литерном массиве фрагмента по
ключевому слову:
а) неструктурированная и утяжеленная вычислениями;
б) неструктурированная но с вычищенным циклом;
в) структурированная и с вычищенным циклом;
г) с использованием оператора поиска подстроки в строке;
Пример. На рис. 3.22 помещены программы поиска элементов литерного
массива, которые содержат в качестве подстроки, заданное ключевое слово (см.
запрос на строке 100). Такая операция является ядром многих
информационно-справочных систем. И мы ее уже два раза использовали (см. рис.
1.18 – база
данных на магнитной ленте и рис. 3.16 – база данных на магнитном диске). Анализируемый массив литерных
переменных перебирается от первого до последнего элемента (см. цикл с
параметром на рис. 3.22). Пустые элементы массива отбраковываются (см.
альтернативу на строках 120-160 на рис. 3.22, а,б,в), а в заполненных
отыскивается ключевое слово.
По вариантам рис. 3.22, а, г можно проследить процесс оптимизации
программы:
первый шаг
– чистка
цикла (сравни варианты рис. 3.22, а и б);
второй шаг
– структурирование
программы (сравни варианты рис. 3.22, б и в); этот шаг, правда, приведет к
потере быстродействия программы, если не перейти к структурной версии Бейсика;
третий шаг
– использование
специального оператора POS поиска подстроки S в строке массива N, начиная с
первого символа его текущего элемента.
Обрати внимание на строку 130 на рис. 3.22, в. В ней переменная
увеличивается на единицу. Это довольно распространенный оператор работы со
счетчиком – взгляни, например,
на программу "Частотный словарь" на рис. 0.35. В языке Турбо-Бейсик
появились специальные "быстрые" операторы изменения значения
переменной на единицу без использования десятичной арифметики.
Персональный компьютер называют первым в истории человечества
инструментом для усиления ума. В программировании же, как ни в какой другой
области человеческой деятельности, сплелись так тесно и наука, и искусство.
Отсылая более дотошных читателей к книгам [9, 10, 17, 23, 27, 34], в следующих
шестнадцати советах попробуем приоткрыть начинающему программисту область,
которую мы довольно условно обозначили в названии главы.
Утро вечера мудреней.
Совершенствуя алгоритм или программу, знай меру. Помни!
Проникновение ЭВМ во все области нашей жизни приводит и к негативным
последствиям. Одно из них
– рост
числа нервных заболеваний на компьютерной почве. Умей себя останавливать на
бесконечном пути оптимизации программ. Спорная во многих других случаях
пословица "Лучшее
– враг
хорошего" здесь весьма уместна. Санчо Панса такую ситуацию предвидел и
изрек: "Если ты собрался за шерстью, смотри, как бы тебя самого не
остригли".
От программистов часто можно услышать, что машина сама по себе – дура. Она начинает
осмысленно что-то делать только после того, как в нее "вдохнут жизнь" – введут составленную
человеком программу. Но в одном машина бесспорно умнее человека – когда ее отключают, она
напрочь "забывает" о той проблеме, над которой только что работала.
Она отдыхает.
Отключая машину, давай покой и ей, и себе. Помни, что утро вечера
мудреней. Умей переключать свои мысли, заведи себе какое-нибудь хобби, не
связанное с компьютерами.
Подобные советы легче давать, чем их придерживаться. Ты уже,
конечно, заметил, читатель, что в своих программах мы чвпасто не следуем своим
же советам. И дело здесь не только в том, что в книге нередко встречаются
взаимоисключающие советы. Мы старались давать читателям какие-то альтернативные
решения, полагая, что они сами выберут из них то, что более подходит не только
для их машины, но и к их характеру, вкусу, темпераменту, наконец.
С компьютеризацией в нашу жизнь, в наш язык вошли такие термины,
как мониторы, принтеры, драйверы, узеры и т.д. и т.п. Вот еще один термин из
программистского дела, которому вряд ли можно подобрать русский эквивалент:
KISS-принцип. С поцелуями он ничего общего не имеет, хотя хорошее отношение к
компьютеру в нем можно усмотреть. KISS – это аббревиатура английской фразы "Keep it simple, stupid – делай это проще,
дурачок!". Она призывает решать поставленные задачи более простыми
методами, прибегать к изощренным алгоритмам и программным средствам только в
крайнем случае. При этом нужно всегда помнить, что любой наисложнейший алгоритм
машина с помощью транслятора сведет к комбинации полусотни простейших операций,
которым обучен ее процессор. Чем проще алгоритм, тем быстрее и эффективнее
машина это сделает.
Для человека простой алгоритм тоже предпочтительней по ряду
причин. Вот две из них.
Во-первых, простые алгоритмы дают субъективное ощущение их
надежности. Программу, такой алгоритм реализующую, легко "прощупать
руками", мысленно "прокрутить".
Во-вторых, в простых алгоритмах, как правило, несложно снять
ограничения, принятые при их разработке. Проиллюстрируем эти два тезиса
примером.
На рис. 4.1 представлены программы поиска наибольшего общего
делителя (НОД) чисел.
Первые две программы (рис. 4.1, а и б) ищут НОД двух чисел, причем
первая – по
сложному, но "быстрому" алгоритму, а вторая – по простому, но
"медленному". Алгоритм программы на рис. 4.1, а следующий: если А
больше В, то нужно от А отнять В. Если после этого окажется, что В больше А, то
нужно от В отнять А. Все это необходимо повторять до тех пор (цикл
"до", хотя здесь можно с таким же успехом применить и цикл
"пока"), пока А не станет равным В. Истинность этого алгоритма
доказать не так просто, она по крайней мере не лежит на поверхности. Обычно в
этот алгоритм просто верят, принимая во внимание авторитет Евклида (он его
разработал) и тот "железный" факт, что программа на рис. 4.1, а
работает безупречно.
Алгоритм программы на рис. 4.1, б виден невооруженным глазом: одно
из чисел (А) принимается за НОД, а если это не так (строки 3 и 4), то ответ корректируется
вычитанием единицы из кандидата в НОД. Этот метод проб и ошибок мы подробнее
рассмотрим в совете 48.
Программа на рис. 4.1, б предназначена для поиска наибольшего
общего делителя двух чисел А и В. Но ее очень просто переделать для работы с любым
количеством исходных чисел, объединив их в массив (рис. 4.1, в). Поиск
наименьшего из вводимых исходных чисел (строки 4 и 5) – это "гарнир", т.е.
операция, не составляющая сущность алгоритма, а только его оптимизирующая.
Переделать программу на рис. 4.1, а для поиска наибольшего общего
делителя многих, а не только двух чисел, тоже можно. Но эта модернизация будет
сложней (см. программу определения формулы химического соединения на рис.
0.28), что и требовалось доказать.
Не допусти, чтобы у тебя на работе отобрали ПК.
Большинство персональных компьютеров в нашей стране персональными
называются по недоразумению. В наших университетах и институтах намного больше
персональных кабинетов и автомобилей, чем персональных компьютеров. Многие
преподаватели пока только мечтают о персональном гибком диске. Что уж тут
говорить о персональном компьютере.
Первый абзац совета включен в рукопись книги в июле 1988 г. Мы
очень надеемся, что к моменту выхода книги в свет он устареет, и его мы выкинем
из окончательного варианта текста. Но надежд, честно говоря, мало.
Персональный компьютер отличается от любого другого не тем, что он
умещается на столе, а тем, что при работе на нем никто не "дышит тебе в
затылок", поглядывая в график работы на нем сотрудников подразделения. Это
принципиально новая технология использования вычислительной техники.
Но если даже тебе предоставили на работе ЭВМ в безраздельное
личное пользование, превратив тебя из простого человека в пользователя, то
часто тебя не освобождают от обязанности отчитываться перед кем-то за ее
загрузку.
Предлагаем вниманию читателей программу на рис. 4.2.
Рис. 4.2. BASIC-программа "Секретарша"
Некоторые могут рассматривать ее как некое средство научной
организации труда, другие же могут запускать ее в период между решением
серьезных задач для того, чтобы не писать в журнал загрузки ЭВМ
"липу". Требуете, мол, ежесуточной десятичасовой нагрузки машины, – вот и получайте ее.
Непронумерованный совет. Помни! Есть компьютеры, даже
персональные, фиксирующие незаметно для пользователя не только общее время
своей наработки, но и отдельные виды работ: работу с редактором, с пакетами
прикладных программ, собственно программирование и пр. Такой учет нужен,
конечно, не для поиска "липовых" записей в журнале загрузки
(необходимость в нем, кстати, при этом отпадает), а для совершенствования вновь
проектируемых ЭВМ.
Программа на рис. 4.2 превращает ЭВМ в секретаря, напоминающего о
делах дня.
Работая по ней, нужно в начале рабочего дня поделиться с машиной
планами на день (см. строки 2-5), после этого ЭВМ будет держать на дисплее
информацию о ближайшем деле и подавать звуковой сигнал (см. строку 14) в момент
его начала и окончания.
Программа на рис. 4.2 превращает ЭВМ в будильник стоимостью
несколько тысяч рублей. Это, отметим еще раз, далеко не самый оптимальный
способ использования компьютера, даже персонального. Но это, по крайней мере,
лучше, чем если бы компьютер простаивал, а его хозяин в это время разрывался на
части, неправильно спланировав свой рабочий день.
Коротко о программе.
Блок 1 (строка 1)
– резервирование
места для хранения в памяти ЭВМ названий мероприятий дня (массив А), времени их
начала (первая строка массива S) и окончания (вторая строка массива S);
выставление часов компьютера. ЭВМ хранит сроки, переведя их в секунды,
прошедшие от полуночи.
Блок 2 (строки 2-9) – начальный диалог человека с машиной, сообщающего ей свои планы на
день. ЭВМ следит, чтобы не было накладок, – чтобы новое мероприятие начиналось не раньше конца предыдущего и
не позже начала нового (см. условный переход на строке 7).
Блок 3 (строки 10 и 11) – информация о делах дня, которую в память ЭВМ можно вводить в любом
удобном для нас порядке. ЭВМ же их рассортирует по хронологии, руководствуясь
операторами строк 10 и 11, где реализован известный метод "пузырька"
с признаком Р.
Блок 4 (строки 12-16) – монолог компьютера, "поглядывающего на часы" и
сообщающего человеку о начале и конце очередного дела дня. Компьютер "Искра
226", для которого написана программа, говорить не может (а подобные
машины уже созданы [23]), а только подает звуковой сигнал (его код 07 – см. конец строки 14),
призывающий взглянуть на дисплей.
Две подпрограммы блока 5 (строки 17 и 18) позволяют обращаться к
часам ЭВМ и переводить машинное время, убывающее на 2000 единиц за одну
секунду, в привычные часы (Н), минуты (М), секунды (S).
Непронумерованный совет. Выделяй графическими средствами
логические блоки программы (см. рис. 4.2). Не упускай возможности выделить хоть
часть ее структуры (см. цикл "до" на строках 10 и 11).
Задание читателю. Модернизируй программу на рис. 4.2, в, обеспечив
следующие возможности:
а) прерывание монолога машины (блок 4) и возвращение на строку 3 программы
для ввода данных по новому делу; такая необходимость возникает и в том случае,
когда вспомнили о забытом деле, и тогда, когда нужно перенести на более поздний
срок мероприятие, выполнить которое в данный момент невозможно;
б) гибридизацию данной программы с программой, позволяющей вести
дневник, общие черты которой описаны в совете 38;
в) соединение ЭВМ с телефонной сетью так, чтобы машина не просто
напоминала тебе о предстоящем звонке, а сама набирала нужный номер и просила
тебя лишь поднять трубку.
Непронумерованный совет. Прежде чем выполнять данное задание, не в
учебных, а в практических целях, поинтересуйся, нет ли готовых программных
продуктов "Пакет делового человека". Подобные программы уже созданы
давно. Вернись к выполнению задания, если тебя они почему-то не устраивают.
Еще один непронумерованный совет. Влезая в телефонную сеть,
поинтересуйся законодательством на этот счет. Во многих странах среди прочих
ограничений есть и ограничение на прямой контакт телефонной сети с какой-то
другой сетью. В таких случаях используют модем – ухо и язык компьютера, говорящего по телефону через воздух. Но и
это можно делать лишь в том случае, если нет запрещения на передачу по телефону
закодированных (в том числе компьютером) сигналов.
И еще один непронумерованный совет. Помни! Внедрение
вычислительной техники имеет не только технические, экономические, научные и
пр., но и юридические аспекты. Эту проблему мы начали здесь и продолжим в
совете 49.
Предупреждай о выполнении машиной необратимых операторов.
Помни! Информация на диске может стоить дороже самого компьютера,
поэтому выполнение "серьезных" операторов (уничтожение файлов,
переразметка диска, запись новой информации на ленту и т.п.) предваряй
предостерегающими сообщениями на дисплее.
Рис. 4.3. BASIC-программа разметки диска для работы с каталогом
Программа на рис. 4.3 позволяет разметить для работы в режиме
каталога гибкий магнитный диск. После запуска программы (диск должен быть
вставлен в карман R дисковода) раздается звуковой сигнал, экран гаснет (см.
конец строки 20) и появляется вопрос (см. строку 30), который может показаться
несколько оскорбительным только тому, кто никогда не хватался в досаде за
голову, уничтожив по ошибке свои собственные или чужие программы, файлы данных.
"Стреляный" программист после такого вопроса еще раз проверит,
вставил ли он в дисковод тот самый диск, требующий разметки, а не какой-то
другой, и продолжит выполнение программы.
Кроме программных (см. рис. 4.3) есть и аппаратные средства защиты
записей на внешних носителях: паз с выступом на магнитофонной кассете, прорезь
на конверте гибкого диска, специальная кнопка на дисководе. Но "береженого
бог бережет" – еще раз
взгляни на рис. 4.3.
С другой стороны, следует помнить, что, уничтожая файл на диске,
мы его на самом деле не уничтожаем, а "присваиваем ему статус
ликвидированного". Есть специальные сервисные программы "скорой
помощи", позволяющие считывать данные даже из ликвидированных файлов.
Но пусть эта информация не снижает твоей бдительности – еще раз посмотри на строку
30 в программе на рис. 4.3.
Непронумерованный совет. Вставляй в свои программы, подобные той,
какая дана на рис. 4.3, описания страшных историй, знакомство с которыми
отпугнет пользователя от непродуманных действий.
Говорят, что полководческое искусство – это умение принимать
правильные решения при недостатке исходной информации или даже при полном
отсутствии оной.
Язык программирования, где широко используется принцип умолчания,
в чем-то подобен полководцу, например в том, что самый великий полководец
все-таки имел поражения (полная информация гарантирует ну если не победу, то
уклонение от боя), и программирование с широким использованием принципа
умолчания чревато неприятными казусами. Но совсем без принципа умолчания нам не
обойтись. Им пронизан живой язык общения. Когда мы говорим: "Алеша, сходи
за маслом", то мы имеем в виду, что идти нужно в магазин, а не в аптеку,
что нужно захватить деньги, что нужно купить сливочное масло, а не пальмовое
или машинное, что масло должно быть несоленое, что его вообще может и не быть в
магазине, что... Не знаем, кто решится продолжить раскрытие всех умолчаний в
этой фразе.
Если теперь эту фразу произнести с помощью компьютера:
PRINT "АЛЕША, СХОДИ ЗА МАСЛОМ"
то добавятся новые умолчания:
а) фразу нужно вывести на дисплей, а не на бумагу принтера и не на
магнитный слой диска;
б) фразу нужно пропечатать заглавными буквами, ранее оговоренным
шрифтом;
в) отсутствие замыкающих кавычек не должно приниматься за ошибку – там кроме них ничего другого
не может быть и т.д. и т.п.
Без принципа умолчания, когда машина сама решает, как ей быть,
"вспоминая" предыдущие случаи, программист превратился бы в невольного
зануду, сто раз объясняющего машине, как ей поступить.
Но с другой стороны, при написании программ на Бейсике будь
аскетом – не
пользуйся принципом умолчания или, по крайней мере, не злоупотребляй им. И
пусть тебя не смущает то, что авторы книги сами-то при написании программ
принцип умолчания использовали довольно-таки широко: часто не ставили,
например, параметр цикла после слова NEXT, не замыкали кавычками литерную
константу, стоящую в конце строки, не указывали единичный шаг в цикле с
параметром и т.д.
Злоупотреблять принципом умолчания не следует по крайней мере по
двум причинам: во-первых, он по-разному толкуется на разных версиях Бейсика;
во-вторых, начинающий программист, работающий с Бейсиком, где так широко
используется принцип умолчания, рано или поздно будет вынужден прибегнуть к
услугам других языков программирования (Паскаль, Фортран, Ада и др.). В них же
принципы умолчания почти совсем не используются. Привычка (недаром ее называют
второй натурой) к принципу умолчания здесь станет причиной многих ошибок. В
английском судопроизводстве, например, широко практикуется принцип прецедента,
когда судья, вынося приговор, вспоминает похожие случаи. Но никто не станет
автоматически переносить этот принцип в суды других стран. В чужой монастырь со
своим уставом не ходят.
Иногда стоит так написать программу, чтобы в ней долго никто не
смог разобраться.
Начнем с двух историй.
Один из авторов этой книги, будучи в загранкомандировке, приобрел
там себе персональный компьютер, к которому среди всего прочего была приложена
кассета с играми. Уже в Москве одну из этих игр знакомые автора захотели
адаптировать к отечественным компьютерам – "калькам" с вышеупомянутого импортного. Это удалось
сделать без особого труда, но в заставке игры упоминалось об ее авторе и
программисте. Знакомые одного из авторов ради любопытства попробовали заменить
заморские имена своими. В программе такую замену было сделать несложно, но
после запуска программы машина аккуратно печатала заставку с новыми именами,
также аккуратно их вычеркивала, помещая затем на это место старые имена. Как
это машина проделывала, приятели автора уяснили только через год, когда
полностью разобрались в ее устройстве. К этому времени их интерес к игре угас.
Вторая история связана с игрой "Животные", описанной в
советах 16 и 17. С алгоритмом этой игры (не с программой, а только с
алгоритмом, который довольно прост) один из авторов познакомился, играя на
английском языке на компьютере Apple.
Непронумерованный совет. Если игру "Животные" вести на
иностранном языке, то это будет занимательным и эффективным средством овладения
иноязычной лексикой.
Попытка разобраться в Бейсик-программе игры "Животные"
на компьютере Apple пресекались первой строкой с одним-единственным оператором
GOTO 60000. Такой строки в программе не было, да и не могло быть совсем в
данной версии Бейсика. Попытки запуска программы не с самой первой строки
прерывались сообщением об ошибках, а запуск программы командой RUN срабатывал
(!?). Сейчас у нас появился опыт программирования, но нет доступа к компьютеру
Apple, и можно только предполагать, что в программе игры был участок, который
сначала запускал индикатор ошибок, а потом сам себя стирал, заметая следы. При
выполнении оператора GOTO 60000 возникала программная ошибка, следствием
которой был не аварийный останов, а переход на определенный участок программы.
Всю программу с тайными участками нельзя была просмотреть до первого ее
запуска, так как программа загружалась с диска с самозапуском, как и многие другие
игровые программы.
Непронумерованный совет. Если ты в публикуемой программе
используешь чужую идею, то упомяни об этом, даже если ты не знаешь имя автора.
Автору будет приятно знать, что его идея жива, даже если не упомянуто его имя и
даже если авторами себя почувствуют сотни других людей.
Традиционный способ сделать из программы тайну – это написать и отладить ее
на каком-либо языке программирования, а затем откомпилировать ее в выполнимый
файл, работающий не в среде языка-родителя, а в среде операционной системы
машины.
Любопытство
– не
порок, а метод исследования.
BASIC-программы, помещенные в книге, написаны на различных версиях
этого алгоритмического языка [19, 25, 31, 35, 42, 43 и 47]. Но работая с этим
языком, совсем не обязательно углубляться в руководство по программированию,
чтобы уяснить детали выполнения того или иного оператора, конструкции на данной
версии языка. Учитывая, что на Бейсике с машиной обычно разговаривают
посредством синхронного переводчика – интерпретатора, часто бывает проще испытать незнакомый оператор в
непосредственном режиме. Так, например, выполнение оператора PRINT SIN (30)
подскажет, в градусах, градах или радианах воспринимается аргумент
тригонометрическими функциями, а PRINUSING "#"; 8.9 – округляет ли аргумент
оператор печати с форматом, оператора PRINT А=1=2 – совмещены ли транслятором функции булевых и числовых переменных в
переменных одного типа.
У такого рода любопытства есть еще одно веское основание. Дело в
том, что различные инструкции и книги по программированию, как правило, пишутся
не системными программистами-разработчиками трансляторов, а совсем другими
людьми. Эта книга – не
исключение. Системные программисты, создав транслятор, обычно сразу остывают к
своему детищу и бросают силы на разработку нового. Для них истина – в движении. Описатели же
трансляторов не по злой воле, а по неполному знанию что-то упускают.
Будь любопытен с языком, но боже тебя сохрани использовать
полученные таким образом сведения без дальнейшей проверки в готовых программах.
Может оказаться, что эффект от нового оператора или конструкции побочен или
стиснут какими-то ограничениями.
Если ты, читатель, работаешь с каким-то одним компьютером, то
отметь (или дополни) в своем словаре те служебные слова и обороты, которые
подходят к твоей версии языка. Словарь поможет пропустить через твой компьютер
программы книги, написанные на других версиях.
Проруби в своем дисплее еще одно окно в мир программирования.
При отладке программ удобно пользоваться двумя дисплеями. На
первом дисплее можно "держать" фрагмент программы, а вторым дисплеем
"ползать" по всей программе, выясняя, какие изменения нужно внести в
зафиксированный на первом дисплее фрагмент. Такая технология программирования
заодно сэкономит бумагу принтера: так, при одном дисплее часто приходится
"ползать" по твердым копиям, которые постоянно нужно обновлять в
процессе редактирования и расширения программы.
Прочтя этот совет, читатель скажет: "Совет хороший, но где вы
видели два дисплея, тем более на персональном компьютере". Да, два дисплея – это роскошь, но вот
несколько окон на одном дисплее можно увидеть часто. Так, если при работе с
Турбо-версиями Бейсика и Паскаля в момент отладки программы нажать клавишу
помощи (ею, как правило, бывает первая функциональная клавиша), то на дисплее
появится новый мини-дисплей (окно) с подробной информацией об операторе (или
конструкции), помеченном курсором. Повторное нажатие клавиши помощи или клавиши
ESP стирает справочную информацию в окне с восстановлением исходного текста.
Программировать на таком языке можно без всяких дополнительных
руководств. Такая технология кроме предоставления пользователю удобств
ликвидирует противоречие, описанное в предыдущем совете: системный программист,
разрабатывая транслятор, одновременно пишет и инструкцию по его использованию.
Прием использования окон на экране дисплея, кроме того, широко
используется в диалогах, поддерживаемых прикладными программами (игры,
редакторы, картотеки, деловая графика и др.). Умело используй этот прием и в
своих прикладных программах-самоделках.
На ошибках не только учатся, но и строят алгоритмы.
Создавая черновой вариант программы, мы неизбежно делаем в ней
ошибки, которые затем исправляем при отладке программы.
Но есть особый род ошибок, которые специально предусматриваются в
программах. Это делается для реализации метода проб и ошибок. Ряд программ
книги иллюстрируют его.
Работая по программе "Семейный бюджет" на рис. 3.13,
машина пытается (см. строку 19) открыть для считывания несуществующий файл.
Пробует и ошибается. Но это не приводит к аварийному останову работы по
программе, а заставляет машину искать новый файл, имя которого совпадает с
календарной датой, когда подводилась черта под семейными доходами и расходами.
Для такой работы какого-то другого, более целенаправленного алгоритма и не
придумаешь – ведь
машина не знает, в какие конкретно дни семья подводила итог своих расходов и
доходов. Машина знает только дату начала ведения записей и дату последней
записи, которые хранятся на магнитном диске файлом с именем ГРАНИЦЫ (см. строку
4 на рис. 3.13). Если эти границы изменяются, что имеет место при новой записи
в книгу, то содержимое файла ГРАНИЦЫ также меняется (см. строку 12). В
программе на рис. 3.13, как заметит внимательный читатель, нет
операторов-анализаторов места и вида ошибок, но есть несколько операторов
включения индикаторов ошибок с разными адресами переходов. Это сделано в целях
избежания "поломки" программы при возможной перенумерации строк (об
этом см. в совете 1F).
Программа на рис. 1.11 реализует игру "Животные" с
хранением бинарного дерева вопросов и ответов на магнитном диске. В ней попытка
считывания несуществующего файла, так же как и в программе "Семейный
бюджет" на рис. 3.13, не приводит к аварийному останову, а передает
управление на участок программы наращивания ветви бинарного дерева. Машина при
этом учится на своей ошибке, узнавая от человека разницу между задуманным
человеком и последним предложением машины.
Программа "Энциклопедический словарь" на рис. 1.20
прощает пользователю целый "букет" ошибок: попытки открытия для
считывания несуществующего файла, невключенный дисковод, невставленный диск,
неснятую защиту от записи.
Помни! Ошибка ошибке рознь. Умело пользуйся операторами фиксации и
обработка программных ошибок.
Умей видеть в пустяковой на первый взгляд программе возможную
практическую пользу.
По программе, помещенной на рис. 4.4, ведется перевод арабского
числа в римское.
Рис. 4.4. BASIC-программа перевода арабского числа в римское
Другие варианты этой программы см. на рис. 2.12
Задание читателю. Составь программу обратной операции перевода
римского числа в арабское. Алгоритм такого перевода ничем не отличается от
заложенного в программы на рис. 2.12 и 4.4. В этих программах достаточно будет
слегка изменить несколько операторов.
На первый взгляд может показаться, что эти программы не имеют
никакой практической ценности. Ну кому на самом деле придет в голову идея переводить
римское число в арабское с помощью компьютера?! Да и часто ли приходится
сталкиваться нам с римскими числами?! Но, оказывается, в повседневной жизни мы
несколько раз на дню ведем подобные расчеты, когда имеем дело с деньгами. Сумма
денег – это арабское
число, а набор банкнот и монет
– это
римское число. Например,
128 = CXXYIII = сто+десять+десять+пять+рубль+рубль+рубль.
Поэтому программу на рис. 4.4 несложно переделать в программу,
помогающую кассиру выдавать затребованную сумму денег монетами и банкнотами,
имеющимися в кассе (рис. 4.5).
Рис. 4.5. BASIC-программа "Кассир в банке"
Задание читателю. Дополни программу на рис. 4.5 участками
взаимного перевода денежных сумм с одной валюты на другую с учетом курсов валют
и налога (1-3 %) на подобные операции.
Выполняя это задание, читатель столкнется не только и не столько с
проблемами программирования, но и, как это ни покажется странным, с
юридическими проблемами. Ведь при пересчете копеек, сентавос, центов, пфеннигов
и др. появятся дробные части. Как их округлять?!
На заре компьютеризации банковского дела в газетах промелькнула
заметка о том, как один ловкий банковский клерк смог так запрограммировать
компьютер, что эти десятые доли, никак не задевая клиентов, незаметно
перечислялись на тайный счет самого клерка. Так компьютер получил еще одну
специальность – медвежатника,
бесшумно вскрывающего сейфы.
Непронумерованный совет. Создавая "бухгалтерские"
программы, помни, что ошибки в них могут иметь уголовно наказуемые последствия.
Если есть искусство ради искусства, то должно быть и
программирование ради программирования.
Уж коль скоро мы завели разговор о пользе пустяковых на первый
взгляд программ (см. предыдущий совет), то вот тебе читатель в руки игра, придуманная
москвичом А. Ермаковым.
Правила:
два участника игры (А и В) должны написать программу на Бейсике
(можно и на другом языке) из N пронумерованных строк (N – четное).
В распоряжении играющих – по одной булевой переменной А и В, которые в начале игры равны
логическому нулю (FALSE).
Играющие поочередно каждым своим ходом должны заполнить любую
свободную строку одним из двух возможных операторов вида:
для играющего А:
A = not (A) или if A goto K
для играющего В:
B = not (B) или if B goto K
Здесь K-номер строки в пределах от 1 до N.
На других версиях Бейсика булевы переменные можно заменить на
числовые:
if A=O then A=1 else A=O
if A=1 goto K
Если в результате прогонки написанной программы A будет равно B,
то выигрывает участник A, если A=B, – то участник B. Поэтому в целях удобства судейства программу
следует замкнуть операторами print "ВЫИГРАЛ"; : if A=B then print
"A" else print "B".
Зацикливание
– хроническая
болезнь программ с бессистемно расставленными операторами перехода. Если после очередного
хода соперника второй игрок заявит о "зацикленности" написанной к
этому моменту программы и это подтвердится, то заявивший выигрывает. Если это
не подтвердится (при большой программе и неповоротливой машине время такой
проверки можно ограничить), то заявившему засчитывается поражение. После своих
ответных ходов игроки теряют право на заявку о зацикливании. Но если в конце
игры программа все-таки зациклится, то выигрывает участник А.
Вот пример партии для программы в четыре строки, где выиграл В:
1 rem 1 A=not(A) 1 A=not(A) 1
A=not(A) 1 A=not(A)
2 rem 2 rem 2 rem 2 rem 2 if B goto 4
3 rem 3 rem 3 B=not(B) 3 B=not(B) 3
B=not(B)
4 rem 4 rem 4 rem 4 if A goto 1 4
if A goto 1
Игра не требует компьютера, хотя он может существенно упростить
судейство.
Задание читателю. Составь программу, превращающую компьютер в
партнера по описанной игре.
Она стоит особо в ряду компьютерных игр, так как здесь алгоритм не
средство, а объект игры. Поэтому она и называется алгоритмической.
Есть у этой игры и практическая польза, связанная с темой гл. 4.
Некоторые программисты, говорят, обладают особым нюхом на зацикленность
программ. Алгоритмическая игра позволяет этот нюх выработать и
совершенствовать.
Пиши программу так, чтобы ее авторство было видно и без указания
на составителя программы.
В фильмах о разведчиках есть один избитый сюжет, когда о провале
группы узнают по изменению "почерка" радиста, передающего сообщения в
"центр". Считается, что опытные программисты, даже если бы они
захотели нарочно изменить свой, выработанный годами стиль, почерк
программирования, не могут этого сделать.
Этот стиль проявляется во всем: и в наименовании переменных, и в
нумерации строк, и в форме подачи листинга, и в применении тех или иных
алгоритмических приемов. Попытки как-то стандартизировать процесс
программирования успешны в той мере, в какой вообще возможно применение
стандартов к произведениям искусства.
В нашей книге накопилось уже достаточно программ, чтобы поговорить
о стиле программирования на примере алгоритма сортировки одномерного массива:
см. рис. 0.4, 0.17, 0.22, -.26, 2.15 и 3.9. Алгоритмы, заложенные в них,
программные средства, их реализующие, имена переменных – различны. Объединяет их
только одно – перестановка
соседних элементов массива. Такие алгоритмы считаются медленными. В этом легко
убедиться, вспомнив о ручной сортировке, например, игральных карт на руках,
когда мы переставляем местами не соседние, а стоящие на разных концах элементы
массива.
Такой алгоритм (он носит название Шелла – по имени изобретателя)
реализован программой на рис. 4.6.
Рис. 4.6. BASIC-программа сортировки одномерного массива методом
Шелла
Читатель, просмотри еще раз программы сортировки и выбери для себя
ту, который ты будешь в дальнейшем пользоваться, вставляя в свои большие
программы. Не отступай от нее (если на то нет веских причин) не потому, чтобы
был виден твой почерк в программах (программу можно и подписать), а потому,
чтобы делать меньше ошибок, чтобы программы писались быстро и качественно. Ведь
выписывая чужое имя, мы медлим и часто ошибаемся. Подписываемся же мы не думая
и без ошибок.
Ищи у классиков темы для программирования.
Многие начинающие программисты сталкиваются с очень интересной
проблемой – поиском
тем для программирования. Мы им можем посоветовать прочесть книги [4, 6, 12,
17, 26, 29, 37], а также нижеприведенный рассказ.
Рассказ владельца домашнего компьютера
Когда я покупал персональный компьютер, мне казалось, что он станет
моим добрым помощником, будет работать на меня денно и нощно. Теперь же мне все
больше кажется, что дело обстоит совсем наоборот, что это я должен денно и
нощно работать на свой компьютер, должен подыскивать все новые задачи, которые
он решал бы, и приносить их ему, словно бесчисленные жертвы ненасытному Молоху.
Назовите мне любую повседневную ситуацию – будь то размен денег или
покупка билета в кинотеатр, перевод иностранного слова или игра в
"крестики-нолики"
– и
я скажу вам, что так или иначе отобразил ее в своих программах.
Я и книги-то теперь читаю, кажется, только затем, чтобы найти в
них какой-нибудь сюжет для новой программы. Приведу два диалога, подтолкнувших
к созданию одной из них.
Подколесин: Ну, а кроме этой, других там нет никаких?
Фекла: Да какой же тебе еще? Уж это что ни есть лучшая.
Подколесин: Будто уж самая лучшая?
Фекла: Хоть по всему свету исходи, такой не найдешь.
Подколесин: Подумаем, подумаем, матушка. Приходи-ка послезавтра.
Мы с тобой, знаешь, опять вот эдак: я полежу, а ты расскажешь...
Фекла: Да помилуй, отец! Уж вот третий месяц хожу к тебе, а
проку-то ни насколько. Все сидит в халате да трубку знай себе покуривает.
Подколесин: А ты думаешь, небось, что жениться все равно что
"Эй, Степан, подай сапоги!" Натянул на ноги, да и пошел? Нужно
порассудить, порассмотреть.
Н.В. Гоголь, "Женитьба".
Устинья Наумовна. Пожалуй, уж коли тебе такой аппетит, найдем тебе
и благородного. Какого тебе: посолидней или поподжаристей?
Липочка. Ничего и потоще, был бы собою не мал. Конечно, лучше уж
рослого, чем какого-нибудь мухортика. И пуще всего, Устинья Наумовна, чтобы не
курносого, беспременно чтобы был бы брюнет; ну, понятное дело, чтоб и одет был
по-журнальному. (Смотрит в зеркало.) Ах, господи! а сама-то я нынче вся, как
веник, растрепана.
Устинья Наумовна. А есть у меня теперь жених, вот точно такой, как
ты, бралиянтовая, расписываешь: и благородный, и рослый, и брюле.
А.Н.Островский, "Свои люди – сочтемся".
Чувствуете, куда я клоню? Ну, конечно, к тому, что компьютер можно
превратить в электронную сваху. Программу для этого я уже составил – ознакомьтесь (рис. 4.7).
Рис. 4.7. BASIC-программа "Электронная сваха"
Нет, не подумайте, ради бога, что я устраиваю знакомства и браки!
Программу я написал из чистого интереса! Как еще одно упражнение в
программистском искусстве, не более.
Живую сваху, я думаю, компьютер еще не скоро сможет заменить. Но
электронная сваха может дать живой фору в объеме информации по клиентам,
скорости подбора пары, а главное – в бескорыстии. Не знаю, научится ли она когда-нибудь разговаривать
так же живо, как достопамятная Фекла Ивановна или Устинья Наумовна. Мой
компьютер, по крайней мере, на это не способен.
Зато какая четкость вопросов! Компьютерная!
Да вы сами прочтите их – вот они, в самом начале программы, в строках со 2-й по 10-ю.
Расставлены по-порядку, в виде элементов литерного массива Р. Не нравятся – можете изменить их или
дополнить.
С 11-й строки начинается работа программы, отображаемая на
дисплее. Все сведения о клиентуре – на магнитном диске. Надежно и удобно. С файла под именем
"М" считывается его содержимое – число карточек М в картотеке, 12-й строкой на дисплей выводится
информация о количестве карточек, а 13-й – меню.
Хотите занести данные о себе в картотеку? Милости просим. Вводите единицу,
нажимайте клавишу "Ввод", а как нажмете, переменная А станет равной
1, и оператор выбора в конце 13-й строки препроводит вас на первую указанную в
нем строку, на 14-ю. Здесь машина спросит про ваш пол, про возраст, и какой
рост, и где живете. В программе это вывод элементов литерного массива Р – с первого по шестой, на
дисплее – вопрос за
вопросом. И на каждый будьте любезны дать ответ в цифровой виде, как
подсказывает примечание в скобках: 1 – мужской пол, 2
– женский,
и так по каждому вопросу.
Потом машина запросит вашу фамилию, имя и отчество, затем адрес и
телефон.
Но вот куда и как она все это запишет? Работа у электронной свахи
такая же бойкая, как у живой: что ни день, то новые клиенты, да и старым не век
же в женихах-невестах ходить, вот иной и выбывает, и запись о нем можно
ликвидировать. Так что прежде чем заводить новую карточку, лучше полистать
старые: одна не свободна, а другая уж и свободна, туда и записывать. Программа
и этому обучена, она вам и чистую карточку подыщет, и от ошибок убережется, в
ней на этот случай добрый десяток строк предусмотрено – с 15-й по 21-ю.
Карточки все просматриваются по очереди, и номер в этой очереди – К. Сначала, в 15-й строке,
он полагается нулевым, здесь же настраивается анализатор ошибок – если они и случатся, то
анализироваться будут, начиная с 21-й строки, куда и указано направиться: ON
ERROR E$, N$ GOTO 21; Е
– код
ошибки; N – номер
строки, где она произошла. Потом в 16-й строке номер очереди К увеличивается на
единицу, переводится в литерную форму К по заданному формату. Умысел тут в том,
что каждая карточка в картотеке – это отдельный файл, и имя у него – число, переведенное в литерную форму. Так удобнее перебирать
карточки, если желаешь найти пустую и заполнить ее.
Начинается перебор в 17-й строке. Внимательнее ее читайте, буква
за буквой. R (K$) K$. Не удивляет, что в скобках
не цифра, не число секторов для записи, а буква, и такая же притом, как за
скобками? Это бывает, когда новый файл открывается на месте старого,
ликвидированного, и под тем же именем. Имя у него – номер карточки; что ж не позаимствовать этот же номер для нового
файла, если старый был стерт? Тогда он откроется для записи, потом выполнится
оператор GOTO 19, а там, на 19-й строке, будет произведена запись всех данных о
новом клиенте, то бишь о вас, запись всего числового массива Р и всего
литерного А$( ); так она и производится всем массивом целиком, если в скобках
после его имени – пустота.
Но уж если файл под именем К занят – тогда ошибка. Тогда идем на 21-ю строку, как велит анализатор
ошибок, здесь убеждаемся, что ошибка случилась действительно в 17-й строке, и
переходим на 18-ю. Читайте и ее внимательно: R (5) K$. Эта запись означает, что
на свободном месте диска R нужно выделить пять секторов для записи файла под
именем К. Ошибка и тут возможна, если файл под таким именем на диске R уже
есть. Тогда опять анализатор ошибок шлет нас на 21-ю строку, а с нее переходим
на 16-ю, потому что ошибка случилась в 18-й строке и условие N$ =
"0017" не выполняется; на 21-й же строке совершаем условный переход,
потому что условие N$
– "0018"
выполняется, и оказываемся на 16-й строке. Тут опять увеличиваем номер К на
единицу, и опять все происходит с начала, и так до тех пор, пока не получится
одно из двух: либо файл под именем К есть и уже был когда-то ликвидирован, либо
файла с таким номером нет. Тогда на 190й строке и производим запись данных Р и
А файлом под именем К. Потом, в 20-й строке, будет еще сравнение номера К с
числом карточек М, которое было до записи.
Вот как оно хитро все происходит, только успевай поворачиваться,
благо сваха быстродействующая, электронная.
Если К меньше или равно М, сразу возвращаемся на 13-ю строку. Нет – значит, в картотеке
прибавление, и М должно стать новым, равным К. Надо переписать файл М и
переходить не на 13-ю строку, а на 12-ю, чтобы на дисплее было показано,
сколько теперь карточек в картотеке. А уже оттуда на 13-ю строку: чего изволите
еще?
Хотите изъять свою карточку из картотеки? Вполне объяснимое
желание: вы либо нашли спутника жизни, либо потеряли надежду достичь своего
счастья с помощью электронной свахи, либо устали отвечать на вопросы
неподходящих просителей руки и сердца. Нажимайте клавишу 2 – вот вы уже на 21-й строке.
Тут вводите номер своей карточки К, компьютер переведет его в литерную форму,
найдет файл с таким именем и ликвидирует его: SCRATCH – и все. И опять на 13-ю
строку: что теперь?
Хотите что-то исправить в своей карточке? Понятно и это: люди
каждый год становятся на год старше, получают образование, успевают жениться и
развестись, а клиентами электронной свахи все равно остаются. Она и не
возражает, только карточку надо бы переписать. Опять нажимайте клавишу 2,
стирайте свою старую карточку, потом нажимайте клавишу 1 – заполняйте новую. Примите во
внимание, что ваш номер в картотеке может поменяться, если на диске есть
ликвидированные файлы с меньшим номером.
Хотите поискать по картотеке своего суженого? Увидев на дисплее
меню, нажимайте клавишу 3
– оператор
выбора поведет вас на 23-ю строку, на дисплее появится запрос: ДАННЫЕ ПО
ЖЕЛАЕМОМУ ПАРТНЕРУ? Машина выводит эту фразу, а сама в той же 23-й строке
начинает счет подходящих кандидатур, присваивая счетному индексу Р нулевое
значение. В 24-й строке
– вывод
первого элемента литерного массива Р, потом в цикле вывод остальных элементов,
со второго по шестой, а на дисплее – вопрос за вопросом: опять про пол, про возраст.., совсем как в
14-й строке, когда вы сообщали машине свои данные. Но нет, не совсем так – здесь компьютер беседует с
вами предупредительнее, здесь, начиная со второго вопроса, программа
предоставляет вам дополнительный вариант ответа, нулевой – дескать, этот параметр
партнера не имеет значения.
Машина спрашивает, вы отвечаете, машина ваши ответы запоминает в
виде числового массива Р1. Кончился опрос – переходим на 25-ю строку, начинаем перебирать файлы с номером К от
1 до М, на случай ошибки предусматриваем переход на 28-ю строку, а в 26-й
строке с каждым новым числом К открываем для считывания файл под именем К. Он,
конечно, не откроется, если ликвидирован. Но это ничего, пропустим его:
перейдем по программной ошибке на 28-ю строку, увеличим К на единицу и примемся
за следующий файл. Если ж он не ликвидирован, считаем его содержимое. И там же,
в 26-й строке, начнем сравнивать предложенные в нем параметры Р с желаемыми Р1.
Таких параметров шесть, и просматриваются они в цикле. Если
какой-то из них вам небезразличен, а с желаемым не совпадает, тотчас произойдет
переход на 27-ю строку. Переход закономерный – электронная сваха ваши интересы блюдет неукоснительно, она
предъявит вам только такой вариант, где все интересующие вас параметры
совпадают с желаемыми. Совпадениям тоже ведется счет: при каждом совпадении
счетчик В увеличивается на единицу – на 26-й строке он был установлен на нуль. И если совпадение
состоялось по всем параметрам, к концу цикла В станет равным 6. Это будет
проверено в 27-й строке. Если В меньше 6, машина перейдет на 28-ю строку, а там
согласно оператору NEXT К перелистнет карточку. Если же В=6, то такого
преждевременного перехода на 28-ю строку не произойдет, – машина прежде выведет на
дисплей номер карточки и остановится согласно оператору INPUT. Что вы теперь
введете, неважно, но можете ничего не вводить, литерная переменная А все равно
дальше в программе нигде не встречается, оператор INPUT поставлен тут просто
для остановки. Нажмите только клавишу "Ввод", и машина выдаст вам
имя, отчество и фамилию подходящей для вас особы, адрес и телефон. И отметит
выдачу суммированием Р=Р+1.
Тут уж вам решать, списывать эти данные с дисплея или нет. А
машина, перейдя на 28-ю строку, будет и дальше подыскивать кандидатов в
спутники жизни, пока не просмотрит все карточки. Если нашлась среди них хотя бы
одна подходящая (т.е. Р > 0), компьютер вернется на 23-ю строку, где
записано меню, и будет снова спрашивать, чем он может вам быть полезен; если
нашлась – минует
переход на 13-ю строку, выполнит операторы 30-й строки до конца, выведет на
дисплей просьбу: СМЯГЧИТЕ ВАШИ ТРЕБОВАНИЯ, потом перейдет на 23-ю строку, там
снова запросит ДАННЫЕ ПО ЖЕЛАЕМОМУ ПАРТНЕРУ. Подумайте, чем можно поступиться,
не будьте слишком привередливы
– а
уж машина вас без хорошего предложения не оставит. Ну, а там уж – стерпится, слюбится, еще и
судьбу да компьютер благодарить будете!
Какая вам еще сваха нужна? Моя – что ни на есть лучшая.
Авторы к рассказу владельца домашнего компьютера хотят добавить
следующее. В совете 49 мы затронули юридические аспекты программирования. Здесь
же к ним добавились психологические, этические и прочие.
Непронумерованный совет. Имей возле своего компьютера как минимум
два стула. На одном сиди сам, а на другой приглашай присесть юриста, лингвиста,
психолога, экономиста, юмориста и т.д. и т.п. Давай им опробовать свои
программы и внимательно выслушивай их замечания.
В приведенном рассказе мы дали владельцу домашнего компьютера
возможность подробным образом рассказать о своей программе. Его выражения с
точки зрения строгого программиста не совсем корректны ("Компьютер перешел
на такую-то строку", например), непоследовательны, сбивчивы.
Непронумерованный совет. Иногда бывает очень полезно перед
серьезным изучением нового языка или операционной системы попросить кого-то
рассказать о решении какой-либо несложной задачи. После этого не будешь
испытывать мистического страха перед терминами специальной литературы.
Если ты не знаешь, как поступить в трудной жизненной ситуации, то
смоделируй ее на компьютере.
У авторов произошел тут спор, но не по сути совета (хотя и здесь
есть о чем поспорить), а по примеру, его иллюстрирующему. Один из авторов
считал, что такой "кровожадный" пример не стоит приводить в книге.
Другой же утверждал, что читатель, пообщавшись с персональным компьютером, уже
повидал много "крови", играя в игры типа "Звездные войны",
"Космические пришельцы" и т.д. Иллюстрирующий пример – "Тройственная
дуэль" – описан
даже в книгах для школьников [6], но наиболее полно эта логическая головоломка
описана в [17]. К тому же сейчас дуэли запрещены. К счастью, а может быть, и к
несчастью. Иногда так хочется вызвать на дуэль директора завода, выпустившего
бракованный компьютер, и представителя госприемки. Вот вам и тройственная
дуэль, о которой пойдет речь.
Но сначала непронумерованный совет.
Помни, что, имея под рукой ЭВМ, можно проводить расчеты по
алгоритмам, на реализацию которых в докомпьютерную эру решались только
одержимые какой-то идеей единицы. Из литературы известно, что французский
естествоиспытатель Бюффон (XVIII век) подбросил 4040 раз монетку и подсчитал,
что орел выпал 2048 раз, а решка – 1992. Английский статистик К. Пирсон описал серии бросаний монетки
в 12 000 и 24 000 раз. В первом случае орел выпал 6019, а во втором 12 013 раз.
Тот же Бюффон определил число PI очень интересным способом. Он
провел на большом листе бумаги параллельные равноотстоящие прямые линии и стал
бросать на него случайным образом иголку длиной, равной шагу между линиями,
подсчитывая общее число бросаний (N) и число попаданий иголки на одну из линий
(N1). Теория вероятностей подсказывает, что в отношении N1/N заложено число PI,
которое и пытался определить Бюффон столь необычным способом.
Определить число PI методом статистических испытаний (или методом
Монте-Карло – так
теперь называют методы расчетов, использованные Бюффоном) можно на машине с качественным
генератором псевдослучайных чисел (RND-функция). Нужно в квадрат вписать
четвертинку круга и бросать в него случайным образом камешки. Козьма Прутков в
подобной ситуации, когда камешки бросались в воду, советовал "Смотреть на
круги, ими образуемые; иначе такое занятие будет пустою забавою".
Воспользуемся советом и будем считать количество попаданий в четвертинку круга.
Отношение числа попаданий к общему числу бросков, помноженное на четыре, должно
приближаться к числу PI.
Непронумерованный совет. Проводя статистические испытания на
компьютере, помни о том, что ты не только испытываешь модель, но и проверяешь
качество генератора случайных чисел.
Увертюра к совету окончилась. Приступаем к сути.
Программа на рис. 4.8 реализует модель многосторонней дуэли. Вот
ее описание из [17] при трех дуэлянтах.
Рис. 4.8. BASIC-программа "Многосторонняя дуэль"
Сэм, Билл и Джон (ниже обозначенные буквами С, Б и Д) договорились
сразиться на дуэли втроем по следующим правилам:
1) жеребьевка определяет, кто стреляет первым, вторым, третьим;
2) далее они располагаются на одинаковых расстояниях друг от
друга;
3) обмениваются выстрелами по очереди, определенной жребием, пока
двое не будут убиты, и
4) очередной стреляющий может стрелять в любого из остальных.
Известно, что С
– снайпер
и никогда не промахивается с данной дистанции, Б – поражает мишень в 80 % случаев, а Д – приблизительно в 50 % случаев. Какова наилучшая стратегия для
каждого из участников и каковы вероятности их выживания, если они следуют
оптимальным стратегиям?
В этой дуэли у Сэма и Билла могут быть две тактики поведения (1 и
2). Первая (назовем ее случайной) – это когда стреляющий ничего не знает о меткости соперников и целит
в случайного. Вторая (бей в меткого) – когда дуэлянту известно о том, кто как стреляет, и он метит в
соперника с наивысшими стрелковыми качествами в надежде остаться tete = a =
tete с наихудшим стрелком.
Джон может следовать третьей (3), хитрой тактике. Чтобы получить
наивысшие шансы выйти победителем из дуэли, он должен намеренно стрелять в
воздух, пока двое его соперников живы. Ведь очередной стреляющий будет бить не
в него, а в более меткого соперника. После того как С или Б будет убит, Джону
нужно будет показать все, на что он способен. В такой ситуации его шансы выжить
составляют по крайней мере 50 %, если он остался наедине с Сэмом, и еще больше,
если с Биллом.
Реализуется эта модель программой на рис. 4.8. Результаты ее
прогонки на разных ЭВМ сведены в таблицу (рис. 4.9).
Рис. 4.9. Результаты испытания программы "Многосторонняя
дуэль" на различных машинах
Несколько слов о программе.
Параметры трех участников дуэли хранятся в четырех массивах:
вещественном массиве М – меткость участников;
целочисленном массиве Т – номер используемой в дуэли тактики;
литерном по форме и логическом по содержанию массиве Р – статус дуэлянта:
"ЖИВ" ─ еще
живой, "УБИТ" ─ уже
убит;
целочисленном массиве F – число побед.
Генератор псевдослучайных чисел в программе используется для:
определения первого стреляющего в очередной дуэли (строка 7);
определения направления очередности последующих выстрелов:
Джон-Сэм-Билл (Д-С-Б) или Сэм-Джон-Билл (С-Д-Б) (строка 8);
имитации выстрела в цель с заданной вероятностью попадания (строка
21).
После запуска программы на дисплее поползут колонки информации (строки
30 и 32): номер очередной дуэли, имяер дуэлянта-победителя в ней и вероятности
выживания участников в прошедших дуэлях.
Тип ЭВМ
MZ = 700
MZ = 700
MZ = 700
ИСКРА 226
ТЕОРИЯ
MZ = 700
MZ = 700
MZ = 700
MZ = 700
MZ = 700
ИСКРА 226
MZ = 700
MZ = 700
MZ = 700
ИСКРА 226
ИСКРА 226
MZ = 700
MZ = 700
MZ = 700
ИСКРА 226
MZ = 700
ИСКРА 226
MZ = 700
ИСКРА 226
ИСКРА 226
ТЕОРИЯ
Тактика поведения
ХИТРАЯ
В МЕТКОГО
СЛУЧАЙНАЯ
Очередность выстрелов
СЛУЧАЙНАЯ
Д-Б-С
Д-Б-С
Д-Б-С
Д-Б-С
Д-Б-С
Д-Б-С
Д-С-Б
Д-С-Б
Д-С-Б
Д-С-Б
Д-С-Б
СЛУЧАЙНАЯ
Д-Б-С
Д-Б-С
Д-С-Б
Д-С-Б
СЛУЧАЙНАЯ
Число дуэлей
2525
1160
8436
5000
БЕСКОНЕЧНОЕ
3649
3440
1193
439
1041
5000
684
1004
24 636
5000
5000
6027
2383
6027
1000
2061
1000
1014
1000
3954
БЕСКОНЕЧНОЕ
Вероятность выживания, %
М = 1(С)
30,0198
30,5172
30,4300
30,8200
30,0000
23,6229
22,8500
22,2100
22,1000
25,4600
21,4070
36,4000
37,4500
36,5400
36,5200
37,2000
23,9600
24,9700
23,9600
24,9000
21,6900
21,7000
28,3000
25,9000
43,3730
43,4780
М = 0,8(Б)
17,7822
17,4138
17,8000
18,2200
17,7770
24,1710
25,2300
22,7200
24,8300
26,5130
23,1670
12,4300
10,5600
12,1000
12,7600
11,2000
31,4300
31,3500
31,4300
30,0000
31,3400
31,7000
29,3900
27,5000
34,4460
34,7830
М = 0,5(Д)
52,1791
52,0690
51,7700
50,2600
52,2200
52,2061
51,9200
55,0700
53,0800
48,0300
55,4250
51,1700
51,9900
51,3600
50,7200
51,6800
44,6200
43,6800
44,6200
45,1000
46,9700
46,6000
42,3100
46,6000
22,1800
21,7390
Рис. 4.9.
Два непронумерованных совета.
Выводить промежуточные результаты на экран дисплея полезно не только
по причинам, описанным в совете 21, но и для того, чтобы контролировать ход
статистических испытаний и прерывать их, когда появится уверенность в
ненужности новых опытов.
Помни! Заменять натурный эксперимент на численный с использованием
ЭВМ нужно не только (не столько) в тех случаях, когда натурный эксперимент
длится очень долго (бросание монеты или иголки, например), или когда он очень
дорог, но и тогда, когда натурный эксперимент проводить вообще нельзя
(многосторонняя дуэль, например).
Задание читателю. Хоть программа на рис. 4.8 и запрашивает любое
число дуэлянтов (строка 3), но если их больше трех, модель многосторонней дуэли
дает сбои из-за невозможности переставить местами четырех и более дуэлянтов
перед очередной дуэлью. Сними это ограничение.
Этот совет мы специально облекли в форму рассказа о том, как
специалист решил с помощью ПЭВМ свою профессиональную задачу. Надеемся, что
читатель сам из рассказа сможет выявить суть совета и не одну, связанную с
темой гл. 4.
Компьютер планирует выпуск себе подобных
Я работаю в плановом отделе завода, которому поручили выпускать
ЭВМ, включая персональные. Для нас это что-то вроде ширпотреба, но заказ
считается важным в свете требований по компьютеризации народного хозяйства.
Планируя предстоящий выпуск ЭВМ, я столкнулся с проблемой такого
рода. Дело в том, что мы можем выпускать машины четырех различных типов. У нас
для этого уже есть документация, оснастка и прочее. В конструкцию машин входит
много различных электронных элементов, включая пять типов дефицитных больших
интегральных схем (БИС), фонды на которые строго лимитируются.
В таблицу я поместил основные характеристики ЭВМ (стоимость машины
и емкость оперативного запоминающего устройства – ОЗУ) и число разных БИС, необходимых для изготовления одной
машины:
N ЭВМ
1
2
3
4
Стоимость ЭВМ, руб.
600
2500
25 000
60 000
Емкость ОЗУ, Кбайт
16
32
64
512
Число БИС, шт.
1
1
1
-
-
2
-
-
1
1
3
4
8
-
-
4
-
-
4
8
5
3
8
15
64
Максимальный объем выпуска ЭВМ, шт.
100
62
20
12
Первые две машины относятся к классу персональных. Высокие
стоимости третьей, и особенно четвертой ЭВМ связаны с тем, что в комплекты их
поставок входят дорогие периферийные устройства: накопители на магнитных лентах
и дисках, графопостроители, принтеры и др.
Наш завод, правда, не будет делать эту периферию сам, мы ее
получаем готовую. Нам надо будет только дооборудовать ее устройствами
сопряжения с ЭВМ. Тем не менее, стоимость комплектующих изделий переходит в
стоимость самой готовой машины и фигурирует в отчетах о выполнении заводского
плана со всеми вытекающими из этого последствиями.
БИС N 1 и 2
– это
центральные процессоры ЭВМ, БИС N 3 и 4 – блоки памяти, БИС N 5 входит в состав узлов сопряжения с
периферией.
Заводу выделены фонды на получение ста БИС N 1, двадцати БИС N 2,
пятисот БИС N 3, ста БИС N 4 и тысячи двухсот БИС N 5. Диктуемые этими
ограничениями выкладки и дают цифры, приведенные в последнем столбце таблицы, – максимально возможный выпуск
машин одного типа.
На наш завод может выпускать одновременно все четыре типа ЭВМ. Моя
задача как экономиста заключается в нахождении оптимального плана выпуска
компьютеров, учитывающего интересы как потребителей, так и завода.
Во-первых, необходимо насытить рынок компьютерами. Это задача
государственной важности. В этом случае нужно максимизировать функцию вида
К1 + К2 + К3 + К4.
Здесь К1-К4
– планируемый
объем выпуска четырех типов ЭВМ, шт.
Во-вторых, не нужно забывать и об интересах завода, финансовое
положение которого, фонды поощрения и стимулирования зависят от выполнения
плана в рублях. Этот показатель ругают все по делу и без дела. Я лично подхожу
к этому вопросу по-философски и радуюсь, что у нас план в рублях, а не в
тоннах, как в некоторых других отраслях машиностроения. Не могу даже
представить, какие ЭВМ тогда бы мы выпускали.
Целевая функция по критерию стоимости имеет вид
600К1 + 2500К2 + 25 000К3 + 60 000К4.
Можно, наконец, выпускать компьютеры с максимальной общей емкостью
ОЗУ. Этот показатель наряду с быстродействием машины для потребителей часто
оказывается определяющим при покупке ЭВМ.
Соответствующая целевая функция такова:
16К1 + 32К2 + 64К3 + 512К4.
В идеале нужно, конечно, так организовать производство, чтобы
максимально удовлетворить потребителей и по количеству, и по номенклатуре машин
и дать при этом заводу хорошую прибыль. Но соответствующую целевую функцию я
составить не могу. Да это вряд ли можно сделать, если над нами будет висеть
дамоклов меч лимитов.
Дефицит перечисленных БИС накладывает ограничения на выпуск ЭВМ.
Математически это описывается системой неравенств:
К1 + К2 <= 100,
К3 + К4 <= 20,
4К1 + 8К2 <= 500,
4К3 + 8К4 <= 100,
3К1 + 8К2 + 15К3 + 64К4 <= 1200.
В эту систему могут входить и равенства. В этом случае его нужно
записать дважды – со
знаками < и >. Равенство получается тогда, например, когда органы
народного контроля требуют полного использования выделенных заводу ресурсов,
принимая наши возражения за отговорки.
Есть много книг, где описаны методы решения подобной проблемы. ее
называют задачей линейного программирования, так как целевая функция и
ограничения зависят от переменных линейно. Разработано много программ решения
этой задачи на ЭВМ.
Область существования переменных целевой функции, определяемая
ограничениями-неравенствами, имеет вид N-мерного многогранника.
Известно, что значения переменных, максимизирующие или
минимизирующие целевую функцию, являются координатами одной из вершин данного
многогранника. В нашей задаче о компьютерах он четырехмерный, с девятью гранями
(пять граней – ограничения
по БИС и четыре грани
– отсекающие
минусовые значения числа компьютеров).
ЭВМ, решая задачу линейного программирования, как бы ощупывает
вершины многогранника и запоминает ту, где целевая функция минимальна или
максимальна.
Программу (рис. 4.10) для решения этой задачи я взял из комплекта
программ, приложенного к нашей "Искре 226". Компьютер после запуска
программы подсказывает пользователю, что ему надо делать.
Рис. 4.10. BASIC-программа "Линейное программирование"
На дисплее сначала появляется:
ЧИСЛО ПЕРЕМЕННЫХ ОГРАНИЧЕНИЙ?
В нашей задаче их 4 и 5 – набираем эти числа.
1-е ОГРАНИЧЕНИЕ
- гласит надпись на дисплее. Потом поочередно в ряд загораются
символы переменных с приписанным справа знаком умножения. После появления
каждого символа нужно на клавиатуре набрать значение коэффициента, который на
эту переменную умножается. Так постепенно на дисплее выписывается левая часть
формулы первого ограничения. Потом необходимо указать знак неравенства в этой
формуле, потом значение ее правой части – запрос о ней тоже появляется на дисплее.
Далее то же самое следует сделать для второго и следующих
ограничений, затем таким же образом вводится целевая функция, и, наконец, нужно
указать, какова цель расчета
– минимизировать
или максимизировать целевую функцию (см. строку 6).
Программа на рис. 4.10 решает задачу так называемым
симплекс-методом. Вот результаты расчета:
Критерий оптимизации
Число ЭВМ
Общая стоимость
Емкость памяти
План выпуска ЭВМ по типам, шт.
1
100
-
80
2
-
62,5
20
3
20
5,882
-
4
-
9,559
12,5
Задача, как я и опасался, осталась фактически нерешенной. Как
истолковать тысячные доли ЭВМ
– как
совет поставлять компьютеры россыпью?
Знакомые пользователи ЭВМ посоветовали решить задачу не путем
прогонки Бейсик-программы (рис. 4.10), а более современным способом на машине
IВМ РС с помощью пакета "Эврика", что я и сделал. Вот отчет
компьютера о поиске плана выпуска ЭВМ с максимальным суммарным объемом ОЗУ (RAM
по-английски):
..........................................
Eureka: The Solver, Version 1.0
Thursday May 18, 1989, 10:31 am.
Name of input file: C:\EKA\LINPROG.EKA
..........................................
RAM = 16*k1 + 32*k2 + 64*k3 + 512*k4
S max (RAM)
k1 >= 0
k2 >= 0
k3 >= 0
k4 >= 0
k1 + k2 <= 100
k3 + k4 <= 20
4*k1 + 8*k2 <= 500
4*k3 + 8*k4 <= 100
3*k1 + 8*k2 + 15*k3 + 64*k4 <= 1200
...........................................
Solution:
Variables Values
k1 = 63,640932
k2 = 26,161671
k3 = 0,025416978
k4 = 12,484925
RAM = 8249,3369
Confidence level = 75, 6 %
All constraints satisfied.
Warning: time limit exceeded.
Непронумерованный совет. Хочешь "быть на ты" с
компьютером – учи
английский язык или, по крайней мере, запомни несколько десятков английских
терминов.
При работе в программной среде "Эврика" задача записывается
на естественном языке (см. среднюю часть отчета), что существенно упрощает
диалог человека с машиной. Но полученное решение (solution) меня не
удовлетворило и на этот раз.
Откуда берутся эти тысячные доли компьютеров, сообразить нетрудно.
Возникают они потому, что некоторые вершины нашего многогранника имеют
нецелочисленные координаты. Дробные результаты расчета надо округлять. Но в
какую сторону?
Может быть, стоит изменить саму постановку задачи и искать
оптимальные значения плановых показателей в точках с целочисленными
координатами, расположенными внутри многогранника?
Эту мысль мне подсказал один простой пример: нужно найти
наибольший член последовательности N N, где N – числа натурального ряда. Методы математического анализа велят
заменить последовательность N N функцией X X и искать ее максимум традиционным
способом: взять производную и определить ее корень. Он будет равен числу е =
2,718... – основанию
натуральных логарифмов. Ответ исходной задачи лежит в одной из соседних точек с
целочисленными координатами
– 2
и 3. Проверим, что подходит: 2 = 1,4142, а 3 3 = 1,4422. Это значит, что ответ
равен трем.
Такого результата, кстати, можно было бы достичь без замены
последовательности функцией и без всякого дифференцирования, простым перебором,
взяв несколько первых членов последовательности и заметив, что дальше она
стремится к нулю.
Подобное можно проделать и в нашей задаче о выпуске электронных
вычислительных машин. Пусть сам компьютер переберет все возможные варианты
выпуска ЭВМ и отберет те, которые максимизируют или минимизируют ту или иную
целевую функцию, смотря в каком аспекте решается задача – стараемся ли мы увеличить
число выпускаемых ЭВМ, емкость их памяти, уменьшить их себестоимость и т.д.
Рис. 4.11. BASIC-программа "Компьютеры"
Вот программа (рис. 4.11), реализующая такой подход. Целевая
функция – суммарный
объем выпуска ЭВМ (строка 110). Ее следует максимизировать.
В программе три вложенных друг в друга цикла, параметры которых – количество выпускаемых ЭВМ
второго, третьего и четвертого типов. В строках 20-70 для каждого сочетания
этих параметров определяется возможное число машин первого типа с учетом
остатка ресурсов по БИС.
В строках 80-100 проверяется выполнение ограничений. Они, кстати, как
и сама целевая функция, могут быть и нелинейными. Так оно чаще всего и бывает
на практике: стоимость комплектующих изделий может быть поставщиком снижена,
если заказывать большую серию; стоимость ЭВМ будет падать при массовом
производстве.
В строке 110 каждый раз подсчитывается значение целевой функции.
Если новый результат окажется больше найденного ранее, то он выводится на
дисплей.
Варианты планов, полученные как методом перебора (см. рис. 4.11),
так и симплекс-методом (см. рис. 4.10), сведены в итоговую таблицу (рис. 4.12).
Рис.4.12. Результаты планирования выпуска компьютеров различными
методами и по различным критериям
Какие выводы можно сделать, глядя на нее?
Симплекс-метод плохо приспособлен для работы с целочисленными
переменными. При поиске оптимального плана с максимальным числом машин он утаил
очень выгодные для завода варианты. Можно, оказывается, выпускать те же 120
компьютеров, но большей общей стоимостью – сравните позиции 1 и 2 итоговой таблицы.
Самая дефицитная БИС – четвертая. Она почти во всех случаях ограничивает выпуск ЭВМ.
Нужно постараться выбить на нее дополнительные фонды или у кого-то на что-то
сменять.
При планировании выпуска ЭВМ, если не удастся достать
дополнительных БИС, нужно руководствоваться позициями 3 или 8. при этом можно
максимально учесть и интересы потребителей (выпуск большого числа недорогих
ЭВМ), и интересы производства (вал в рублях).
Позиция 6 с
отрицательным числом ЭВМ первого типа получилась по недоразумению. В программе
на рис. 4.11 величина К1 не проверялась на неотрицательность, что вообще-то
следовало бы сделать. Вот машина и выдала, на первый взгляд, чепуху. Но только
на первый! Компьютер тут выступает в роли беса-соблазнителя, нашептывая
"интересную" идею: "Купи где-нибудь на стороне 27 дешевых ЭВМ
первого типа, разбери их, а дефицитные БИС пусти на производство более дорогих
машин. План по валу в рублях будет перевыполнен!"
Любая программа на 99 % состоит из служебных слов и имен.
Служебных слов не так уж много (обычно несколько десятков), именам же нет
числа. В них и еще в алгоритме и проявляется фантазия автора программы.
Имя, память, воспоминанье... Этими понятиями, сняв с них
поэтическую оболочку, оперируют программисты, например, создавая компьютерные
базы данных.
Одна из первых проблем, которую решают родители новорожденного, ─ это
выбор имени для него. Накладывает ли имя отпечаток на характер человека, на его
судьбу ─ вопрос
сложный и спорный. Можно услышать совершенно противоположные мнения на этот
счет. Но тот факт, что имя, даваемое новому файлу (переменной, функции,
процедуре), может сыграть огромную роль в его судьбе, не будет оспаривать ни
один более менее опытный программист.
Об этом мы и поговорим в этой главе.
Дав файлам удачные имена, можно создать базу данных с удобными
средствами записи, поиска и исправления.
Имя файла на магнитном диске обычно не превышает по длине восьми
или шестнадцати символов. Но и эта короткая информация при умелом ее
использовании позволяет реализовывать сложные алгоритмы сбора, обработки,
хранения и выдачи данных с использованием внешнего носителя памяти ─
накопителя на магнитных дисках, например. Программы книги позволяют этот факт
проиллюстрировать.
Программа на рис. 1.11 позволяет сыграть с компьютером в игру
"Животные". Файлы, хранящие вопросы и ответы, имеют имена,
совпадающие с номерами ветвей бинарного дерева.
Программа на рис. 1.20 создает на магнитном диске файлы, имена
которых ─ ключевые
слова поиска. Это позволяет быстро найти на диске нужную информацию.
Программа на рис. 3.13 превращает ЭВМ в семейную книгу расходов и
доходов. Имя файла ─
календарная дата дня, когда вводились в ЭВМ данные, занесенные потом в файл.
Программа на рис. 4.7 позволяет создать на диске банк брачных
объявлений. Имена файлов, хранящих данные о клиентах электронной свахи, ─ это их
номера, записанные в литерной форме: 00001234, например.
Рис.XXII.
Рис.5.1. BASIC-программа "Электронная поваренная книга"
Программа на рис. 5.1 превращает ЭВМ в электронную поваренную
книгу, где тексты кулинарных рецептов записываются на магнитный диск файлами,
имена которых отображают порядок их поступления в книгу.
Чем может быть полезна такая книга?
В нее можно записывать кулинарные рецепты (строки 5-12), чтобы в
нужный момент быстро отыскать подходящие, стоит только сообщить машине его
название (строка 5). Впрочем, чаще всего поиск ведется не по тому, что есть в
книге, а по тому, что есть в доме. На строках 15-21 программы проверяется,
пересекаются ли множества "продукты в доме" (литерный массив I) и
"продукты рецепта" (литерный массив N). Конечно, в идеале нужно,
чтобы эти множества не просто пересекались (это проверяется двойным циклом на
строке 18), ─
необходимо, чтобы множество "продукты рецепта" входило в множество
"продукты в доме". Но такой идеальный случай на практике крайне
редок, и человеку нужно будет самому, глядя на рецепт (он выводится на дисплей
операторами 19-й строки), решить, позволит ли ему недостаток части продуктов
приготовить блюдо.
На магнитном диске кроме пронумерованных текстов рецептов (имя
файлов, их хранящих, совпадает с номером) есть еще один файл с именем КУХНЯ,
содержащий одну-единственную числовую переменную ─ текущее число рецептов на
диске. Этот файл до первого запуска программы нужно внепрограммно создать на
магнитном диске после его разметки для работы в режиме каталога (см. совет 43).
При желании такую работу можно делать и программно ─ см.
совет 5D.
Проследим, как на магнитный диск записывается новый кулинарный
рецепт, благо стрелки, объединяющие самостоятельные блоки программы, позволяют
легко это сделать.
После запуска программы объявляются массивы, а литерным переменным
указывается, какой длины они должны быть, иначе по принципу умолчания длина их
будет равна 16 символам (строка 1). Под сам текст рецепта отводится не более
250 символов, включая, естественно, пробелы (см. конец строки 1). Затем с
магнитного диска считывается содержимое файла КУХНЯ ─ число
рецептов в кулинарной книге (строка 2). Этой информацией машина делится с
человеком (см. второй блок программы ─ строку 3). Третий блок
программы ─ меню, но
еще не приготовленных блюд, а только режимов работы с электронной поваренной
книгой (строка 4). На запрос машины человек нажимает клавишу "1"
(запись нового рецепта), и управление программой передается на строку 5. Запрос
названия блюда (строка 5) выделен в самостоятельный блок, так как эта операция
проводится и при записи нового рецепта (А=1), и при поиске рецепта по его названию
(А=2). Этот блок имеет два входа и два выхода ─ на строку 6, с которой
начинается блок запроса остальных атрибутов рецепта и запись его на диск, и на
строку 15, к блоку информационного поиска, соответственно.
Атрибуты нового рецепта включают число необходимых составляющих
(строка 6), их параметры (строка 7, они вводятся циклом ─ см.
строку 8), сам текст рецепта и на сколько порций он рассчитан (строка 9). Новый
рецепт получает имя F (строка 10) и записывается на магнитный диск (строка 11).
Так как рецептов на диске стало на один больше, операторы строки 12 меняют
содержимое файла КУХНЯ и передают управление второму блоку программы на строку
3.
Блочная структура программы "Электронная поваренная
книга" позволяет легко проследить порядок выполнения 2-го и 3-го режимов
работы.
Описывая работу программы на рис. 5.1, мы ввели термин
"множество", не расшифровав его. Это особый тип переменных, который
на Бейсике реализуется, как правило, массивами. На языке Паскаль под множество
выделен специальный тип переменных вида
ГОД set of ЗИМА, ВЕСНА, ЛЕТО, ОСЕНЬ
АВТОРЫ set of ОЧКОВ, ПУХНАЧЕВ
Есть на Pascal'е операции с множествами. Так, если в нашей
программе на рис. 5.1 для того, чтобы узнать, совпадает ли какой-то один
продукт в доме с продуктом в рецепте, понадобилось два вложенных цикла (строка
18), то на Паскале такая операция будет не сложнее перемножения двух чисел,
например. Но есть у "паскалевского" множества и крупный недостаток ─ в
заголовке программы нужно перечислить, какие элементы в него входят. А грош
цена тому множеству, элементы которого заранее известны. Для программы
"Электронная поваренная книга" это не подходит ─ кто
знает, что завтра выбросят в магазине, и кто знает, какой рецепт продиктует
твоей жене ее подруга.
Стоит в программе изменить несколько констант ─ и ей
может найтись новое применение.
Рис.XXIII.
Читатель, постарайся открыть эту книгу на двух страницах ─ там, где
помещена программа "Электронная сваха" (рис. 4.7), и на новой
программе "Электронный квартирный маклер" на рис. 5.2. Это совершенно
разные программы, но внешне они отличаются мало.
Рис.5.2. BASIC-программа "Электронный квартирный маклер"
Во-первых, изменено содержание литерного
массива Р (строки 2-9), а во-вторых, нет деления квартир на "женихов и
невест". Вот и все изменения.
Задание читателю.
Измени программу на рис. 5.2 так, чтобы по ней можно было не только
организовывать простые обмены, но и составлять сложные цепочки, по которым,
например, в комнату в коммуналке из двухкомнатной квартиры переезжает одинокий
мужчина с вредными привычками, на освободившуюся площадь въезжает пара
пенсионеров из четырехкомнатной квартиры, куда переезжает из коммуналки семья
программиста, поселяя в одну из комнат персональный компьютер.
Нет ничего тайного, что не стало бы явным.
Рис.XXIV.
Записать на диск файл
так, чтобы никто другой, кроме тебя, не смог считать его или без твоего
согласия что-то в нем изменить, можно, дав ему секретное имя, например,
"ОООООООО". Секрет здесь в том, что часть букв О в имени файла ─
латинские, а часть ─ русские,
и об этом знаешь лишь ты.
Мы специально в
секретном имени использовали буквы О, а не другие, имеющие похожее написание и
в латинском, и в русском алфавите. Дело в том, что буквы А, В, С, Е, Н, К, Р и
др. ЭВМ часто выписывает на экране дисплея или на бумаге принтера несколько
по-разному в различных алфавитах. Глаз опытного программиста эту разницу всегда
уловит. Еще более опытный программист сможет раскрыть секрет, даже не напрягая
зрение. Он просто распечатает коды сомнительных букв, как это делает программа
на рис.5.3.
Рис.5.3. BASIC-программа печати шестнадцатиричных кодов ─ символов
литерной переменной (с протоколом прогонки)
Из протокола ее прогонки
видно, что первый раз слово МАМА было введено на латинском регистре, а второй
раз ─ на
русском. Заодно машина пропечатала невидимые миру пробелы (их шестнадцатеричный
код ─ 20),
дополняющие литерные переменные до 16 знаков.
Непронумерованный
совет. Вывести кавычки в составе литерной константы можно, записав в программу
не их самих, а их код.
На многих машинах
практикуется штатный способ защиты Бейсик-программ от посторонних взглядов.
Готовую программу так записывают на диске, что ее потом можно только считывать
и работать по ней. Просмотреть же ее, а тем более исправить, нельзя. Но, как
это всегда бывает в жизни, появление мер тут же вызывает появление контрмер ─ есть
специальные программы-отмычки, позволяющие открыть закрытый файл.
Резюме. Всем известно,
что замки предназначены для честных людей. Жуликам они не помеха. Честный
человек подойдет к незнакомой запертой двери, подергает ее и отойдет. Такую
манеру поведения следует перенести и на работу с компьютером.
"Если на клетке со львом увидишь надпись "Буйвол" ─ не верь
глазам своим." К. Прутков
Дав нехитрый совет о тайном имени файла (см. выше, совет 52),
авторы невольно затронули очень важную проблему желательности и возможности
использования букв кириллицы в идентификаторах языка.
Если ты, читатель, возьмешь какую-нибудь переводную книгу по
какому-то языку программирования и увидишь в ней программы с русскими именами
переменных, функций, процедур и т.д., то верь глазам своим. Авторам известны
только три типа компиляторов по их отношению к буквам кириллицы.
Первая группа компиляторов предназначена для
"чистокровных" западных машин и не имеет дело с буквами кириллицы.
Вторая группа компиляторов допускает буквы кириллицы, но только в
комментариях. Попытки использования таких букв в идентификаторах нещадно
пресекаются при компиляции программ.
Компиляторы третьей группы сдаются перед настойчивостью
славян-программистов, пытающихся давать переменным и процедурам осмысленные и
понятные многим имена (см. рис. 5.4, а с известной нам программой о рыбаках и
рыбках), но переиначивают их (имена) на латинский лад: РЫБАК ─ RYBAK,
ПРИЗНАК ─ PRIZNAK
и т.д. (рис. 5.4, б).
Рис.5.4. BASIC-программа Рыбаки и рыбка:
а) имена переменных написаны русскими буквами;
б) магнитный перевод имен переменных.
И это делается, конечно, не в дискриминационных целях. Дело тут в
том, что иначе (оставление букв кириллицы в именах) произойдет путаница: два
имени МАМА (см. протокол на рис. 5.3), например, написанные на разных
регистрах, машиной будут восприниматься за разные, а человеком ─ за одно
и то же. Нельзя же каждый раз прибегать к отмычкам, описанным в предыдущем
совете.
Тем не менее у программистов получила распространение технология,
когда черновик программы пишется с русскими именами (без Ч, Ш, Щ, Э и Ю, так как
у них нет двойников в латинском алфавите ─ см. рис. 5.4, а), такая
русифицированная программа с "говорящими" именами вводится в ЭВМ,
компилятор которой англизирует ее (см. рис. 5.4, б). Но тем не менее разбор и
анализ черновика упрощается. Сказанное относится ко всем "заморским"
языкам программирования. А пока только эти у нас в ходу.
Бейсик-интерпретатор микроЭВМ "Искра 226", например,
относится ко второй группе трансляторов, но допускает буквы кириллицы в именах
файлов. Это позволило авторам, во-первых, дать совет 52, во-вторых, написать
программу на рис. 3.16 простейшей базы данных и, главное, вселило надежду, что
все-таки появятся трансляторы языков высокого уровня, не допускающие
дискриминацию букв кириллицы, даже невольную.
Если мы уж затронули такую важную и больную тему, как лексическая
основа языков программирования, то следует упомянуть не только об
идентификаторах, но и о служебных словах. Некоторые полагают, что замена
импортных служебных слов на их национальные эквиваленты облегчит освоение языка
программирования. Но это иллюзия. Вот тебе, читатель, программа на рис. 5.5 ─ перевод
на русский программы "Частотный словарь", помещенной на рис. 0.5.
Легче тебе в ней разобраться или нет?!
Рис.5.5. BASIC-программа "Частотный словарь" с русским
переводом служебных слов
С другой стороны, нельзя лишать человека выбора национальных
версий языка. Ведь можно придти в библиотеку и взять с полки книгу: Л. Н.
Толстой "Война и мир", либо L. Tolstoi " La guerce et la
paix", либо книгу W. Shakespeare "Hamlet", либо В. Шекспир
"Гамлет".
Давай, читатель, немного помечтаем. Пусть наш московский или
ташкентский школьник подсядет дома к своему персональному компьютеру, вызовет
национальный интерпретатор языка командой БЕЙСИК, напишет программу, подобную
той, какая дана на рис. 5.5, затем наберет слово BASIC, тем самым переведя
программу к международному стандарту (см. рис. 0.5), отпечатает ее на лазерном
принтере и отправит (в двух вариантах) листинги в журналы "Информатика и
образование" и "Bit" по принадлежности.
Не знаем, принесет ли школьнику какую-то пользу такая двойная
работа, но своим русифицированным листингом он потешит самолюбие взрослых
дядей, на которых английское служебное слово действует примерно так, как
красная тряпка на быка. при чтении наших школьных учебников по основам
информатики возникает чувство, что борцы с космополитизмом сороковых и
пятидесятых годов в наше время взяли маленький реванш и отыгрались на
школьниках.
Непронумерованный совет.
Помни! Язык программирования подобен обычному языку общения и в
том отношении, что люди говорят на разных языках, но думают одинаковым образом.
Не связывай алгоритм с его словесной оболочкой.
Используй предыдущий совет о длинных именах переменных с
осторожностью.
Помни, что некоторые Бейсик-машины, допуская довольно длинные
имена переменных (до 40 знаков), идентифицируют их только по первым двум
знакам. В руководствах же по программированию об этом часто стыдливо
умалчивается. Для таких машин переменные программы на рис. 5.4 РЫБАК, РЫБАКИ,
РЫБЫ будут одной и той же переменной.
При переходе к работе с новой версией языка проверь компьютер
конструкцией
AMIN = 2 : AMAX = 1 : ? AMIN
Если на дисплее появится единица, значит, ты работаешь с последней
версией языка, двойка ─ с версией,
экономящей память, отводимую под имена переменных (см. выше). Если же появится
сообщение об ошибке, то ты работаешь с самой первой версией Бейсика,
допускающей максимальную длину идентификатора в два (или даже один) символа.
Подобное явление можно отметить и в других языках
программирования. Так, есть Паскаль-компиляторы, признающие в имени переменной
только первые шесть знаков. Большинство Фортран-компиляторов допускают в имени
переменной также шесть знаков. Последние версии языков допускают в имени
переменной знак подчеркивания, который можно вместо пробела использовать для
разделения слов в сложных идентификаторах: сравни переменную на строке 30 рис.
5.4, а и Паскаль-программы на рис. 0.9, в. Допустимо теперь и одновременное
использование в имени прописных и строчных букв. Все это улучшает
выразительность имен. Сравни:
ИВАНОВИВАНИВАНОВИЧ и
Иванов_Иван_Иванович.
Непронумерованный совет.
Помни! Отличная программа никак не может содержать некрасивые
имена.
Далеко не любое слово может быть идентификатором.
Вот одно стихотворение, с которым мы хотели бы познакомить
читателей. Оно, конечно, не так знаменито, как стихотворение А. С. Пушкина,
давшее название гл. 5
Начало светлое весны...
Лесов зеленые массивы
Цветут. И липы, и осины,
И если помыслы ясны.
Себе присвоил этот май
Права одеть листвою ветки,
И целый месяц в душах метки
Он расставляет невзначай...
И пишется легко строка,
И на этюдник рвутся кисти,
Уходит ложь в обличье истин,
И говорю я ей: пока!
Это стихотворение написал в 60-х годах программист С. А. Маркин.
Полужирным шрифтом в нем выделены служебные слова алгоритмического языка Альфа.
В конце каждой книги по языку программирования читатель может найти список так
называемых зарезервированных слов, которые нельзя или не рекомендуется
использовать в качестве идентификаторов по двум причинам. Во-первых, этого не
допустит транслятор, а во-вторых, если даже при прогонке программы не будет
сообщения об ошибке, то двойственность слов программы может порождать ребусы,
подобные такой альтернативной Паскаль-конструкции:
IF IF THEN THEN ELSE ELSE,
где истинность или ложность (см. предпоследнюю строку
стихотворения С.А. Маркина) вызывает либо процедуру THEN, либо процедуру ELSE,
соответстенно. Читатель, взгляни на программу на рис. 1.13, превращающую
компьютер в часы с боем. В ней слов ВЕЕР используется двояко: в качестве имени
переменной и как служебное слово.
Помни не только имя переменной, но и ее фамилию, т.е. тип.
Взгляни, читатель, на рис. 5.6 с билингвой Бейсик-Фортран решения
задачи о Ханойских башнях.
Рис.5.6. Билингва (BASIC-FORTRAN) решения задачи о Ханойских
башнях без использования рекурсии
В совете 0F мы ее решили с помощью рекурсии. Здесь
же (автор программы на рис. 5.6 Н. Саква из Москвы) эта задача решается без
использования рекурсии, но с использованием числового массива М, элементы
которого показывают положение дисков на стержнях. Если М(5)=2, то значит, что
пятый диск находится на втором (из трех) стержне. Первоначально все диски
насажены на первый стержень (см. строку 3), а затем они по одному переносятся
(см. цикл с параметром на строках 6-13) на второй стержень с использованием
промежуточного третьего так, чтобы диск большего размера не ложился на
"меньшего брата".
Программы на рис. 5.6
идентичны, за исключением строки 8. Здесь в формуле на Бейсике использована
функция INT, отрезающая у положительного числа десятичный хвостик. В формуле на
Фортране такая операция не нужна, так как в этом языке переменные I, J, K, L, M
и N, которые математики часто используют в качестве индексов, по умолчанию
принимаются за целочисленные. А эти переменные, принимая новое числовое
значение, поступают с ним, как ветеринар со щенком боксера (см. выше).
Непронумерованный совет. Адаптируя чужие Бейсик-программы к своей
машине и выбрасывая из них знаки процента, означающие целочисленные переменные
(см. следующий совет), помни, что ты можешь заодно и выкинуть скрытые функции
INT.
В Фортран-программе на рис. 5.6 есть еще одна переменная, перевод
которой из разряда целочисленных в разряд вещественных приведет к неверному
результату. Это массив М. Если бы он не объявлялся по умолчанию целочисленным,
то логическое выражение на строке 11 никогда бы не выполнялось. В Фортране есть
особенность, которая во всех учебниках по этому языку печатается жирным
шрифтом, ─ на
Фортране вещественная единица ─ это вовсе не единица, а
.99999... А есть ситуации, когда почти ─ это еще не все.
Непронумерованный совет. Никогда не применяй на Фортране
логического отношения "равно" к вещественным переменным.
Создатели Бейсика поступили умнее (учимся на чужих ошибках) ─ они не
стали заставлять пользователей заучивать правила, смысл которых становится
понятным только после изучения внутренностей машины, а внесли изменения в
транслятор так, чтобы единица оставалась единицей, как ее ни поверни.
В программах на рис. 5.6 максимальный элемент массива М выбран не
случайно равным 64. Игра "Ханойские башни" появилась в Европе в конце
прошлого века. Популярности игры способствовала легенда о том, что ею заняты
монахи храма брахманов, перемещая 64 золотых кольца на алмазных стержнях.
Решение задачи должно совпасть с концом света. В одной из книг по Паскалю [15]
с описания этой задачи начинается глава о рекурсиях (см. совет 0F). Автор
книги, для того, наверно, чтобы успокоить читателей и позволить им спокойно
решать задачу, сообщает, что на решение задачи о 64 дисках (только на решение,
а не на распечатку ответа) потребуется несколько миллиардов лет. Как мы указали
ранее, решение задачи о перемещении N дисков требует 2 N-1 ходов! При N=64
число ходов ~ 10 19. Вот вам и маленькая программка на рис. 5.6.
Задание читателю. Прикинь, сколько времени потребуется твоей
машине, чтобы решить задачу о 64 дисках на Ханойской башне. Может быть есть
более быстрый алгоритм решения, например, не требующий на перемещение десяти
дисков 1023 ходов.
Скупой платит дважды.
Работая на Бейсике, помни, что использование в программе
целочисленных переменных заметно экономит память машины лишь в том случае, если
эти переменные объединены в массивы. Счет же по программе, как правило, не
ускоряется, так как Бейсик-интерпретаторы имеют одну арифметику и для
вещественных (действительных), и для целочисленных величин.
Программа на рис. 0.5 "Частотный словарь" иллюстрирует
этот совет. Программа (надеемся, читатели не забыли) составляет частотный
словарь введенного с клавиатуры текста и выводит его на дисплей в алфавитном
порядке или в порядке убывания частоты встречи слов словаря в тексте.
В программе задействовано шесть целочисленных переменных:
N ─ число
строк в анализируемом тексте;
I ─ текущий
номер строки текста;
L ─ число
слов в частотном словаре;
H ─ номер
позиции начала слова в строке;
K ─ номер
позиции конца слова в строке;
J ─ текущий
номер слова в частотном словаре.
В программе эти простые
(не индексные) переменные записаны в виде вещественных. Целочисленными (по
форме представления данных) их не сделали по двум причинам.
Во-первых, восприятие листинга программы в этом случае ухудшилось
бы из-за обилия знаков процента в конце имен переменных (см., например, рис.
0.12).
Во-вторых, неизвестно, сэкономило бы это память машины или нет. Целочисленная
переменная требует под свое хранение 4 байта, не считая места под ее имя,
вещественная одинарной точности ─ в два раза больше. Каждый
же знак процента в программе занимает байт памяти машины. Так или иначе ввод в
программу простых целочисленных переменных может сэкономить лишь несколько
десятков байтов памяти. А это для современных компьютеров ─ капля в
море.
Другое дело ─ массивы
индексных переменных. Здесь изменение их типа (перевод из разряда вещественных
в разряд целочисленных) может дать огромную экономию памяти. Может случиться
даже так, что вещественный массив в памяти не умещается, а целочисленный
умещается даже с запасом (переход количества в качество). В программе на рис.
0.5 единственный числовой массив с именем W (см. конец строки 10), хранящий
частоты встречи отдельных слов в тексте, имеет статус целочисленного. Нетрудно
подсчитать, что при N = 1000, например (N ─ число строк анализируемого
текста), использование целочисленного массива W вместо вещественного
высвобождает около 20 Кбайт ОЗУ машины. Это уже существенная экономия.
Умный может совсем не заплатить.
В предыдущем совете
было сказано, что использование целочисленных переменных вместо вещественных
может существенно экономить память машины.
Умей вещественные по
своей сути величины превращать в целочисленные ─ и тебе не понадобится
компьютер с большой емкостью памяти.
Примеры:
Сумму денег (см. программу на рис. 4.5), если их выражать не в
рублях, а в копейках, можно будет хранить не в вещественных, а в целочисленных
переменных.
Если расстояния (см.
программу на рис. 3.6) выражать не в километрах, а метрах или даже миллиметрах
(1,12356 км = 1123,56 м = 1123560 мм) и хранить в целочисленных переменных, то
так можно тоже сэкономить место в ОЗУ машины.
Будь только внимателен
при округлении этих величин.
В некоторых случаях реализовать задуманный алгоритм мешает не
недостаток памяти ЭВМ (см., например, два предыдущих совета), а недостаточная
длина мантиссы используемых переменных. В таких ситуациях могут пригодиться
вещественные переменные двойной точности.
Правда, в современных компьютерах большая емкость памяти и высокое
быстродействие позволили увеличить длину мантиссы вещественной переменной
одинарной точности до 16 знаков (Турбо-Бейсик). Переменная двойной точности
имеет длину в 32 знака соответственно.
Мы уже разбирали в самом начале книги программу (см. рис. 0.12) с
переменными двойной точности. Они отмечаются знаком диеза. Кстати, вещественная
переменная одинарной точности тоже имеет свое отличие ─
восклицательный знак в конце имени, который обычно никогда не ставится по
принципу умолчания. Так вот, в программе на рис. 0.12, по которой отыскивается
максимум многомерной функции методом "Два шага", цель использования
двойной точности не совсем ясна. А вот в программе на рис. 5.7 без таких
переменных не обойтись.
Рис.5.7. BASIC-программа сортировки литерных переменных по
русскому алфавиту: использование вещественных переменных двойной точности
Программа сортирует литерный массив С на заданную глубину G по
русскому алфавиту на ЭВМ с "вшитым" англизированным русским
алфавитом. Алгоритм сортировки основан на формировании рангов элементов
исходного литерного массива (см. начало строки 6), указывающих на место
литерной переменной в отсортированном списке. Вещественный массив N, хранящий
ранги литерных переменных, имеет двойную точность элементов ─ на это
указывает знак диеза в конце его имени. Это позволяет работать с очень высокой
глубиной сортировки исходного литерного массива C ─ вплоть
до глубины, равной длине самого длинного элемента исходного литерного массива.
А это эквивалентно полной сортировке по алфавиту. Литерная переменная не должна
содержать знаков, отсутствующих в списке на строке 2. В противном случае ЭВМ
выдаст сообщение об ошибке и попросит повторить ввод (см. конец строки 5).
Значение ранга литерной переменной (см. начало строки 6) очень
быстро возрастает, и если переменная N имела бы одинарную точность, то при G =
5-7 ее значение перестало бы изменяться.
Есть языки с операторами изменения разрядности мантиссы
вещественной переменной без существенного изменения хранимой величины. Это дает
еще дополнительные возможности для оптимизации программ.
О показателе степени вещественной переменной особых споров нет, 10
в 99 степени ─ обычный
максимальный порядок числа. Эту величину, конечно, можно увеличить, но такие
попытки останавливает известный "медицинский" факт: число атомов в
нашей Вселенной намного меньше 10 в степени 100. В числе одинарной точности
мантисса от показателя степени отделяется буквой Е, а в числе двойной точности ─ буквой
D.
Если мы уж начали в
этом совете разговор о довольно распространенной задаче сортировки по алфавиту
литерного массива, то предлагаем читателю познакомиться еще с двумя программами
для этой цели.
Рис.XXV.
Рис.5.8. BASIC-программа распечатки литерного массива по русскому
алфавиту( с учетом первой буквы)
Программа на рис. 5.8 проводит упорядоченный
вывод литерного массива без его сортировки ─ порядок элементов в массиве
не меняется, но они выводятся на дисплей или принтер в порядке (по первым
буквам элементов), зафиксированном строкой 2. Если машину не учить русскому
алфавиту, а в большинство из них "вшит" англизированный русский, начинающийся
с Ю и кончающийся Ч, то можно воспользоваться программой, показанной на рис.
5.9. Машина выведет на печать из списка литерных переменных С сначала элементы,
начинающиеся с прописных латинских букв от А до Z, затем со строчных, а потом
элементы, начинающиеся со строчных русских букв от Ю до Ч. Закончит машина
вывод списка, ориентируясь на прописные русские буквы. В таком порядке были
закодированы в машине буквы русского и латинского алфавитов.
Новый стандарт на
отечественные персональные ЭВМ, принятый в 1986 г., позволяет проблему
сортировки литерных массивов решать проще. В нем коды русских букв возрастают в
строгом соответствии с алфавитом и сортировка литерного массив ничем не
отличается от такой операции с числовым массивом. Коды возрастают строго, но не
совсем, ─ буква Е
почему-то оказалась в самом конце алфавита за буквой Я. Совсем хорошо ─ тоже
нехорошо.
Программы на рис. 5.8 и
5.9 наиболее эффективно работают на компьютере с принтером, обладающим буферной
памятью (см. совет 39): за время, пока принтер распечатывает найденную литерную
переменную, ЭВМ ищет новую. Скорость печати отсортированного списка не будет
практически отличаться от скорости печати исходного.
Некоторым переменным стоит давать не имена, а прозвища
Прозвище человек от его имени отличается (если не вдаваться в
моральную сторону вопроса) локальным своим действием во времени и пространстве.
Рис.XXVI.
Рис.5.10. BASIC-программа "Вечный календарь"
На рис. 5.10 ты, читатель, увидишь программу "Вечный
календарь", из меню которой видно, на что она годится. Казалось бы, эта
программа создана для удовлетворения праздного любопытства, но это не так, и об
этом говорят большие номера строк двух подпрограмм ─
5000-5006 и 5007-5009.
Непронумерованный совет. Выделяя в подпрограммы отдельные участки
Бейсик-программы, помни о том, что они могут пригодиться и в других программах.
Поэтому сразу давай строкам подпрограмм определенные номера и используй в них
переменные с определенными (часто экзотическими) именами.
В этом совете подчеркнут существенный недостаток основных версий
Бейсика в сравнении с таким языком, как, например, Паскаль, где подпрограммы
(вернее, процедуры) вызываются по имени и имеют свои локальные переменные.
Из-за этого многие профессиональные программисты долго не считали Бейсик
серьезным языком программирования, сохраняя между собой и им большую дистанцию.
Подпрограммы программы
на рис. 5.10 уже использовались нами в программе "Семейный бюджет" на
рис. 3.13, а первая из них (5000-5001) будет задействована в программе
"Картотека приказов" в следующем совете.
Первая подпрограмма на
рис. 5.10 запрашивает календарную дату в литерную переменную D, проверяет ее
"разумность" (см. строку 5001) и определяет по ней номер дня, засылая
его в числовую переменную D (см. строку 5006). Сам этот номер никакой полезной
информации не несет, но по разнице номеров двух дат можно узнать, сколько дней
прошло. В этой подпрограмме литерную и числовую переменные D можно считать
глобальными, так как они используются и в основных программах на рис. 3.13,
5.10 и 5.11, а переменные М и G ─ локальные.
Вторая подпрограмма на
рис. 5.10 проводит обратную операцию ─ по номеру дня (числовая
переменная D) определяет календарную дату, формируя ее в литерной переменной D
(см. строку 5009). Здесь к двум локальным переменным М и G прибавилась третья ─ D5. Дать
ей имя D, D0, D1, D2, D3 или D4 нельзя по причинам, лежащим вне этой
подпрограммы. Во-первых, если вместо D5 записать D, то подпрограмма будет
искажать значение своего входного параметра. А такой побочный эффект
недопустим. D0-D4 также нельзя записать вместо D, так как эти переменные
используются в программе "Семейный бюджет" на рис. 3.13 ─ вызов
процедуры в этом случае искажал бы их значения.
Но профессиональные
программисты и Бейсик идут навстречу друг другу. Последние версии Бейсика имеют
не только подпрограммы, вызывающиеся по номеру строки, но и настоящие процедуры
с именами, с входными и выходными параметрами. В таких процедурах программист
может объявить локальные переменные, функции, процедуры, которые если и
совпадут по имени с глобальными, а также с локальными переменными, функциями,
процедурами других процедур (вот где корень структурности!), то с ними не
перепутаются. На этом основана эффективная технология программирования сверху
вниз, когда программа составляется из отлаженных процедур (модулей), о которых
нужно знать лишь их имя и небольшое число переменных, составляющих их
параметры.
Непронумерованный
совет. Ради быстродействия программы иногда стоит все ее переменные сделать
глобальными. Ведь на создание и ликвидацию локальных переменных требуется
машинное время. Ящерица в сложной ситуации отбросила свой хвост, Анри IV ─
протестантство (см. совет 0В), а программист ─ некоторые элементы
структурированности. Человек, взрослея, теряет свои прозвища, если таковые были
в детстве, ─
локальная переменная по исполнении процедуры тоже теряет и свое имя, и свое
содержание.
Модульность программы
способствует оперативному поиску ошибок в ней, ее модернизации, расширению. Поэтому
целесообразно разбивать на модули даже короткие программы-самоделки, созданные
по технологии "снизу вверх", подобные тем, какие помещены в этой
книге.
"Ну и ну, ─ скажет
читатель, не прочитавший предисловия, ─ эти авторы ─ мастера давать
советы, которым сами-то не следуют!"
Отвечать на самим
поставленный вопрос и легко, и приятно.
Во-первых, такая
непоследовательность ─ атрибут
всех советов, а не только тех, какие относятся к теме книги.
Во-вторых, когда писалась
эта книга, версии Бейсика, допускающие модульность программ, только стали
получать у нас распространение.
В-третьих, отсутствие
модульности в программах книги позволяет авторам дать очередное задание
читателю: переделай некоторые (0.5, 2.13, 3.13, 4.2, 4.7, 4.8, 2.10, 5.1, 5.2,
5.10, 5.11 и др.) программы книги так, чтобы они разбивались на самостоятельные
модули.
Рис.5.11. BASIC-программа "Картотека приказов"
Например, программу "Частотный словарь" (рис. 0.5, 0.35,
0.36, 2.14 и 5.5) следует разбить на такие модули:
1) объявление типов переменных, функций, процедур;
2) вызов процедуры запроса исходного анализируемого текста;
3) вызов процедуры составления частотного словаря;
4) вызов процедуры вывода словаря на внешний носитель: экран
дисплея, бумагу принтера, магнитный слой диска и т.д.
Процедура составления
частотного словаря в свою очередь может состоять из новых процедур: поиска
знаков-разделителей в очередной строке текста, сортировки словаря и др.
Особенно подходят для
разбивки на отдельные процедуры-модули программы с меню, в две из которых
входит наша программа "Вечный календарь", ─
программы "Картотека приказов" и "Семейный бюджет".
Задание читателю. Дополни программу "Вечный календарь"
на рис. 5.10 участками, учитывающими разные сроки перехода различных стран на
новый стиль летоисчисления.
Если сравнить программу
на рис. 5.10 с аналогичной программой на рис. 2.13, то можно заметить, что в
литерной форме выражения календарной даты цифры года поменялись местами с
цифрами дня: "29.07.88" в программе на рис. 2.13 и
"88.07.29" в программе на рис. 5.10. Такую перестановку потребовали
компьютеры, ─ им,
видите ли, так удобнее разбираться в датах, оценивая, что произошло раньше, а
что позже. К такой "компьютеризованной" записи календарных дат потихоньку
привыкает и человек. Можно отметить, что в странах с высокой степенью
компьютеризации год в начале даты записывают уже давно.
Нужно помнить не только имя переменной, но и свое собственное.
Читатель, взгляни на программу на рис. 5.11. Она осуществляет
контроль исполнительской дисциплины, проводя информационный поиск в картотеке
приказов и распоряжений. В картотеке сто карточек (см. первый оператор
программы), имеющих такие записи:
порядковый номер в картотеке (от 1 до 100);
дата выдачи распоряжения (текущий век);
дата выполнения распоряжения (текущий век);
имя человека, отдавшего приказ (до 16 символов);
имя исполнителя приказа (до 16 символов);
суть распоряжения (до ста символов);
номер папки (дела), хранящей дополнительную информацию.
Кроме
того, карточки хранят даты исполнения распоряжений в "машинном" виде ─ номера
соответствующего дня на хронологической оси текущего века. Перевод календарной
даты в номер дня осуществляет подпрограмма, записанная на строках 5000-5006 и
хранящаяся в программе "Вечный календарь" (см. рис. 5.10).
Картотека распоряжений
хранится на магнитном диске файлом с именем КАРТОЧКИ и полностью перегружается
в ОЗУ ЭВМ при запуске программы (строка 2). Литерная переменная D0, считываемая
с диска, хранит календарную дату последней правки картотеки, характеризует ее
"свежесть". Если в процессе работы с картотекой в нее внесли
изменения, то переменная R теряет свое первоначальное нулевое значение и
становится равной единице, что вызывает необходимость перезаписи картотеки на
магнитном диске (см. строку 5).
Программное меню
(строка 4) позволяет работать с картотекой по восьми возможным режимам, не
считая девятого, замыкающего любое меню. Сама же программа состоит из 10 блоков:
блока начального диалога человека с ЭВМ и ЭВМ с дисководом (строки 1-3); блока
программного меню и ветвления по участкам программы для выполнения приказов
человека (строка 5); блока заполнения новой карточки (строки 7-12); блока
снятия с контроля выполненного распоряжения (строка 13); трех небольших блоков
запроса машиной дополнительной информации перед поиском (строки 14, 15 и 16);
блока информационного поиска (строки 17-24) и блока конца программы (строка 6).
Рис.XXVII.
При заполнении новой
карточки машина следит, во-первых, чтобы строки выполнения распоряжений не
датировались вчерашним числом (а так очень часто бывает в реальной жизни) и,
во-вторых, чтобы картотека не переполнялась (строки 9 и 7). Но более важно
давать исполнителям и начальникам "стандартизированные" имена, иначе
будет невозможен информационный поиск. Ведь человек может откликнуться и на имя
"Ваня", и на "Иван", и на "Иван Иванович", и на
"тов. Иванов". Но для машины, работающей по программе на рис. 5.11,
это будут имена совершенно разных людей. Выбери себе единый стиль записи имени ─
"Иванов И. И.", например, и придерживайся его всегда, если какие-то
особые причины не толкают на его изменение.
Задание читателю. Введи
в программу на рис. 5.11 участок контроля за соблюдением установленного
стандарта записи имен начальников и подчиненных.
Непронумерованный
совет. Если банк данных, хранящийся на магнитном диске, может полностью
поместиться в ОЗУ твоей машины, то перед началом информационного поиска
перегрузи его из внешнего носителя в память компьютера. Так можно существенно
ускорить поиск информации.
Взгляни на программу на
рис. 5.1, по которой ведется поиск кулинарных рецептов. Файлы, их хранящие (это
тоже карточки), по очереди считываются с магнитного диска. Данные, записанные в
файлах, проверяются на соответствие условиям поиска: совпадение названий
рецептов или названий ингредиентов.
Информационно-справочная
система с последовательным считыванием отдельных частей банка данных с
магнитного диска, как это делается программой на рис. 5.1, имеет то
преимущество, что объем банка данных ограничен на ОЗУ машины, а емкостью
внешнего носителя памяти. Недостаток же у такой системы весьма существенный ─ низкая
скорость поиска информации. По программе "Картотека приказов" все
карточки один раз целиком перегружаются с диска в ОЗУ, что существенно
увеличивает скорость последующего поиска информации.
Храни информацию о файлах диска на самом диске в главном файле.
Вот типичная картина работы не очень опытного программиста с ЭВМ.
Такой программист, распечатав каталог диска с прикладными программами, часто не
может вспомнить, для чего они предназначены. Ведь имя программы (обычно длиной
до 16 символов) мало что говорит (взгляни еще раз на название этой главы). Программист
начинает по очереди считывать с диска одну программу за другой, просматривает
их листинги или делает пробные запуски. Такой перебор программ и дисков может
длиться довольно долго. Советуем для решения такой проблемы завести на каждом
диске файл-сеньор, хранящий информацию о всех остальных файлах-вассалах,
подобно тому, как это сделано в программе на рис. 5.12.
Рис.5.12. BASIC-программа расшифровки содержимого файлов на диске
Если эту программу запустить, то ЭВМ распечатает имена хранящихся
на диске файлов (строка 10) и запросит имя файла, по которому нужно получить
дополнительную информацию (строка 20). Для этой информации в программе
зарезервированы строки с 30-й по 6999-ю. Меню (строка 7000) позволяет
продолжить поиск, считать найденный файл, переписать программу на диске после
появления нового файла и соответствующих дополнений в программе на рис. 5.12,
которая имеет имя PROGRAM. Считывание найденного программного файла-вассала
ведется в непосредственном режиме, так как программа-сеньор перед этим сама
себя стирает (см. конец строки 7020), уступая место новой программе.
Дисковые операционные системы современных ПК [7], имеющих емкие
винчестерские накопители в 20 и более Мбайт, имеют дополнительные средства
быстрого поиска нужных файлов, копирующие феодальную иерархию типа "Вассал
моего вассала ─ не мой
вассал". Файлы на таком диске (гибкому это не нужно из-за его маленькой
емкости) имеют структуру дерева, каждая ветвь которого может иметь много
подчиненных файлов. Так, если нужно, например, найти программу решения системы
линейных алгебраических уравнений, то можно сделать это по цепочке файлов:
"прикл. прогр." (корень) ─ "математика" ─
"уравнения" ─
"системы" ─
"метод Гаусса". В некоторых домашних компьютерах, пользователи
которых не имеют никакого понятия о файлах, трансляторах и прочем, такая задача
решается просто. После их включения ЭВМ сразу превращается в Бейсик-машину.
После этого нужно вставить в дисковод главный диск и нажать клавишу так
называемого быстрого запуска. Тогда с диска считывается и запускается программа
с меню "Что, мол, угодно!". Вот и все.
Создавая программу, помни об уровне компьютерной грамотности
пользователя.
Читатель, мы просим у тебя прощения. Рассказывая о программах
создания на диске локальных баз данных (рис. 1.18, 3.13, 3.16. 4.7, 5.1 и 5.2),
мы умолчали (точнее, придержали до этого совета) очень важную информацию. Дело
в том, что все эти программы при первом их запуске дадут сбой: аварийный
останов ─
отсутствие файлов с именем КНИГА (рис. 1.8), ГРАНИЦЫ (рис. 3.13), СТРАНИЦЫ
(рис. 3.16), М (рис. 4.7 и 5.2) и КУХНЯ (рис. 5.1). Все перечисленные файлы
нужно будет до первого запуска соответствующих программ создать внепрограммно,
в непосредственном режиме работы с Бейсиком. Но такая работа по плечу далеко не
всем пользователям! Как тут быть?
Если программа создается для неподготовленного пользователя, то,
конечно (см. совет 1F), программист не вправе требовать от него умения
проводить такие довольно сложные операции, как внепрограммное создание файлов
на внешнем носителе. Программист должен позаботиться о том, чтобы машина сама
инициализировала диск при первом обращении к нему. Такой путь, как было уже
отмечено, более приемлем для неопытных пользователей, но имеет тот недостаток,
что при заправке в дисковод по ошибке не того диска, какой нужен при работе по
программе, машина его безжалостно инициализирует ─ создаст на нем лишний файл.
Кроме того, в этом случае программа обременяется операторами, работающими
только при первом ее запуске.
Программа на рис. 5.13 позволяет проводить на ЭВМ экзамен по любой
дисциплине.
Рис.5.13. BASIC-программа "Электронный экзаменатор"
Компьютер задает экзаменуемому вопросы с несколькими вариантами ответов
(строка 19), один из которых верный. При неправильном ответе ЭВМ выдает верный
ответ (строка 23) и задает следующий вопрос (строка 25). В конце экзамена
сообщается число правильных ответов (конец строки 25).
Вопросы с вариантами ответов хранятся на магнитном диске в файлах,
имена которых в литерной форме повторяют порядковые номера их записи на диск.
Обособленный файл с именем ВОПРОСЫ хранит информацию об общем числе вопросов на
диске. Перед первым запуском программы этот файл не нужно организовывать
заранее, внепрограммно, это сделает машина сама. В конце строки 1 стоит
оператор, реагирующий на возможные программные ошибки. Если при запуске
программы на диске, вставленном в карман R дисковода, не будет файла с именем
ВОПРОСЫ, то попытка его открытия для считывания не приведет к аварийному
останову ─
управление программой передается на строку 31. На диске будет создан новый
файл, куда будет занесен нуль ─ вопросов пока нет, но их
можно будет начать записывать.
Несколько слов о программе в целом.
Она состоит из шести блоков: начального диалога ЭВМ с периферией
(строки 1 и 2), контроля заполнения магнитного диска и меню (строки 3 и 4),
записи нового вопроса на диск (строки 5-14), начала участка коррекции вопроса,
записанного на магнитном диске (строка 15), проведения экзамена (строки 16-25)
и блока подпрограмм. Время обдумывания ответа на очередной вопрос машины
ограничено, чтобы экзаменуемый не пытался "пересидеть" экзаменатора.
Неповторяющиеся вопросы с магнитного диска "выуживаются" случайным
образом, чтобы у экзаменуемых создавалось впечатление, что их (вопросов) очень
много (см. цикл "до" с генератором случайных чисел на строке 18).
Непронумерованный совет.
Направляй всегда управление программой после обнаружения ошибки не
на какой-то рабочий участок (как это сделано на рис. 5.13), а на участок
обработки ошибок (см. рис. 1.20). Это исключит ряд неприятностей. Так, наша
программа "Электронный экзаменатор" зациклится, если мы будем
пытаться записывать новый вопрос на диск, заполненный уже до отказа: управление
будет передаваться на 31-ю строку программы вновь и вновь, так как машина не
будет понимать, что от нее хотят. Нужно всегда принимать во внимание не только
факт ошибки, но и ее характер, т.е. смотреть, что находится в литерных
переменных N и Е, если говорить о программе на рис. 5.13.
Хорошо, если переменная помнит не только то, что в нее записали,
но и имя соседки.
Здесь мы расскажем об
очень интересных переменных, которые есть на Паскале, но которых нет на
основных версиях Бейсика. Их называют переменными ссылочного типа данных. С их
помощью легко создавать связанные списки, очереди, кольца и другие столь
интересные и экзотические для начинающих программистов объединения переменных.
Представь себе, что у тебя на руках список фамилий,
отсортированный по алфавиту:
1. Абрамов А. И.
2. Волжин А. И.
3. Дюжев О. Ю.
4. Копылов А. И.
5. Кашинский В. И.
......
123. Яковлев С. С.
и тебе нужно внести в него фамилию Григорьев В. А. Если твой
список составляют элементы литерного массива А, то в такой ситуации нужно будет
сдвинуть вниз все фамилии, начиная со 123-й и кончая 3-й: А(124)=А(123),
A(123)=A(122),..., A(4)=A(3) и, наконец, A(3)="Григорьев В.А.". Но
если учесть, что Абрамов А. И. ссылается (или стоит впереди) на Волжина А.И., а
тот на Дюжева О.Ю. и т.д. (Яковлев С.С. не имеет ссылки, "заземлен",
что делает Паскаль-оператор NIL), то появление новой фамилии не внесет сумятицу
в наш список. Стоит только указать, что Волжин А.И. ссылается теперь на Григорьева
В.А., а он на Дюжева О.Ю.
Читатель теперь понял, конечно, какие удобства дает программисту
ссылочный тип данных, например, для сортировки. Нет нужды ни в каких
перестановках ─ стоит
только хаотически введенным элементам дать ссылку на ближайшего по рангу соседа.
Элементы такой сортировки есть в Паскаль-программе на рис. 5.14.
Рис.5.14. Pacsal─программа сортировки
литерного массива использование переменных ссылочного типа
Связанные списки очень удобны при организации данных в виде деревьев
или даже целых аллей. В программе на рис. 1.10, реализующей игру
"Животные", мы храним дерево ответов и вопросов в массивах. Но
массивы, конечно, ─ далеко
не самая удобная форма хранения деревьев. Массивы, занимая всю память машины,
остаются при этом почти пустыми, "дырявыми", как выражаются
программисты. Кроме того, число ярусов бинарного дерева при этом получается
ограниченным. В программе на рис. 1.10 их всего девять: 2 9-1 = 511. Если все
дерево хранить в ОЗУ машины (а не на диске, как в программе на рис. 1.11), то
связанные списки для игры незаменимы: очередной вопрос хранит ссылки на новые
вопросы при ответах "Да" или "Нет". Такую структуру игры
реализует Паскаль-программа (автор Н. Рыжов), помещенная на рис. 5.15.
Рис.5.15. Pascal─программа игры
"Животные": реализация дерева вопросов и ответов через ссылки
Непронумерованный совет. Если для тебя программы на рис. 5.14 и
5.15 сложны, то почитай книги [9, 10, 15, 39], а потом снова вернись к этим
программам. Цель совета 5Е ─ заинтриговать читателя и
показать, что "есть многое на свете, друг Горацио, что и не снилось нашим
мудрецам". Под мудрецами мы понимаем тех, кто хорошо изучил Бейсик своего
персонального компьютера и считает себя достаточно опытным программистом.
Периодически обновляй записи на внешнем носителе.
В одном из предыдущих советов, а именно в совете 5C, было сказано,
что программа вызывается с внешнего носителя по ее имени.
Но об этом имени нужно вспоминать и по окончании работы с ней,
делая ее перезапись. Периодичность такой перезаписи установить трудно ─ все
зависит и от качества носителя информации, и от устройства считывания, и даже
от того, курят или нет в присутствии персонального компьютера. Дым от сигарет
садится не только на легкие, но и на магнитный слой дисков.
То, что все внешние записи нужно хранить как минимум в двух
экземплярах, ясно, наверно, всем. Мы это даже постеснялись оформить в виде
очередного непронумерованного совета. Но и контрольный экземпляр записи
необходимо периодически обновлять.
Непронумерованный совет.
Щедро делись своими программными находками. Ты можешь за это иметь
чистую материальную выгоду, даже если ты за них не получил ни копейки, ─ потеряв
свою копию программы, ее можно вернуть, взяв у того, кому ты ее раньше дал.
Рис.XXVIII.
Рис.5.16. BASIC- программа проверки скорости реакции человека:
перезапись программы на диске
На рис. 5.16 помещена программа проведения соревнования на лучшую
скорость реакции у N участников, каждому из которых предоставляется N1 попыток
(строка 10). Программа не только засекает скорость реакции, но и наказывает
штрафом (строка 90) слишком нетерпеливых либо слишком хитрых, нажимающих
клавишу клавиатуры раньше подачи сигнала "Пуск!". Машина делает произвольную
паузу между ответов человека на запрос "Готовы?" (строка 50) и
командой "Пуск!" (строка 110). В конце соревнования на дисплей
выводятся результаты участников (строка 240) в порядке занятых мест (строки 220
и 230, где запрограммирована сортировка).
Но это все
не главное, ради чего эта программа появилась в книге. На строке 250 стоит
запрос, не дающий пользователю забыть о последнем совете этой главы.
Давая советы начинающему программисту, мы не могли обойти
вниманием вопросы внешнего оформления программ и протоколов их прогонки.
Имей при необходимости два варианта программы – рабочий и демонстрационный.
На такое раздваивание программ толкают некоторые особенности трансляторов,
в частности стремление некоторых BASIC-машин к экономии своей памяти за счет
выкидывания пробелов в строках, лишних с точки зрения машины.
Сейчас авторы поделятся своими небольшими секретами оформления
листингов программ.
На рис. 6.1, а помещена взятая из [19] программа решения системы
нелинейных алгебраических уравнений модифицированным методом Ньютона.
Рис.6.1. BASIC-программа решения системы линейных уравнений
модифицированным методом Ньютона:
а) исходная программа;
б) программа, подготовленная к публикации;
Программа переписана для ПЭВМ "Искра 266" и отлажена с
использованием данных из первоисточника.
На рис. 6.1, б помещена та же программа, но оформленная так, чтобы
читатель смог в ней без труда разобраться и при необходимости приспособить для
своей машины.
Во-первых, в демонстрационном варианте программы (рис. 6.1, б)
ясно выделены участок инициализации и начального диалога (строки 10-50),
участок решения системы уравнений итерационным методом (цикл "до" на
строках 60-160), участок вывода на дисплей или печати решения задачи
(альтернативы на строках 170-190: ведь решения может и не быть) и участок
подпрограммы, хранящей уравнения анализируемой системы (строки 1000-5000).
Называем другие различия в программах на рис. 6.1.
На строках 50, 70 и 130 на рис. 6.1, б в заголовках цикла с
параметром сохранены пробелы благодаря тому, что в словах FOR латинская буква O
заменена на русский эквивалент. Транслятор машины эти строки не обработал,
выдав сообщение о синтаксической ошибке, но и не выкинул нужные для восприятия
человеком пробелы.
На строках 140, 160 и 170 в словах THEN буква T, как и буква О в
слове FOR, взята из русского алфавита, что также позволило сохранить смысловые
пробелы.
Строки 180 в программе на рис. 6.1, б вообще нет (?!). То, что ты,
читатель, видишь, – это
продолжение строки 170.
Оба листинга (рис. 6.1, а и б) отпечатаны с использованием совета
61. Кроме того, на рис. 6.1, б очерчены рамки структурной диаграммы.
Непронумерованный совет. Не пытайся изготовлять красивые листинги
с помощью текстовых редакторов
– наделаешь
кучу ошибок. Доводи до фотогеничного вида отлаженную и опробованную программу.
При такой технологии могут остаться только грамматические ошибки и опечатки в
комментариях, которые, конечно, тоже нежелательны, но не так страшны, как
программные.
Чтобы получить контрастный листинг, не прибегая к жирной ленте,
которая быстро забивает головку принтера, нужно текст программы (протокол
работы) отпечатать дважды по одному и тому же месту.
Рис.6.2. Образцы одинарной, двойной и тройной печати на матричном
принтере
На рис. 6.2 можно увидеть, как реализуется этот совет при
матричной печати, когда небольшой сдвиг копий не только неизбежен, но и
желателен – точки,
образующие символы, при этом сливаются в линии. Все остальные программы и
протоколы, помещенные в этой книге, выполнены на принтере с
"ромашкой" [28
– т.
3] с повтором печати. Поэтому-то они получились такие четкие.
Современные персональные компьютеры (IBM РС, Роботрон 1715 и др.)
имеют программные и аппаратные средства изменения контрастности печати, формы
листинга и даже смены цвета красящей ленты принтера. Это позволяет красиво
оформить листинги программ и протоколы их прогонки, помня русскую пословицу:
"Встречают по одежке
– провожают
по уму".
Пользуйся возможностями принтера – "шей" программе "красивую одежку".
Примечание.
Прием двойной печати применим только на сравнительно новых
принтерах, бумагопротяжный механизм которых еще не совсем разболтался.
Предыдущий совет о двойной печати можно применять частично для
выделения жирным шрифтом или цветом, например, служебных слов и номеров строк,
выполняющих функции меток, как это сделано в программе на рис. 6.3 поиска на
отрезке A-B минимума одномерной функции методом золотого сечения.
Рис.6.3. BASIC-программа поиска минимума одномерной функции
методом золотого сечения:
а) выделение служебных слов и меток – номеров строк двойной печатью;
б) выделение цветом.
Для такой печати нужно иметь уже не два (см. совет 60), а три
варианта программы: рабочий, часть программы одной жирности шрифта (одного
цвета) и другая часть другой жирности (другого цвета).
Если попросить художника разделить отрезок на две неравные части,
то можно быть уверенным, что он это сделает в золотом соотношении:
A ─── X1 ─────B
где A, B/X1, B = X1,
B/A, X1.
Если попросить компьютер найти оптимум функции на отрезке, можно
также быть уверенным, что первым действием компьютера будет деление интервала
неопределенности в золотом соотношении.
И машину, и человека действовать одинаково заставляет чувство меры
в математическом или эстетическом его понимании. Деление интервала
неопределенности в золотом соотношении имеет очень полезное следствие – при очередном приближении к
точке оптимума достаточно вычислить (или экспериментально определить) только
одно значение анализируемой функции.
Отечественный читатель больше привык, когда алгоритм решения
задачи графически изображают блок-схемой [14], а не структурной диаграммой. Что
ж, лучше поздно, чем никогда,
– на
рис. 6.4 программа решения обыкновенного дифференциального уравнения вписана не
в структурную диаграмму, а в блок-схему.
Рис.6.4. BASIC-программа решения обыкновенного дифференциального
уравнения методом Эйлера, вписанная в блок-схему
В программе два раза использованы циклы "до": запрос
исходных данных с "защитой от дурака" (строки 2-4) и табулирование
искомой функциональной зависимости (строки 7-9). Элементы блок-схем читатель
может увидеть и на рис. 0.6, где они даны в сравнении с элементами структурных
диаграмм.
Сейчас блок-схемы почти не используются, они считаются реликтами
времен неструктурного программирования. Кроме того, есть алгоритмы (см. совет
0F), которые ни блок-схемой, ни структурной диаграммой не изобразишь, – рекурсивные алгоритмы.
При оформлении листинга программы можно подчеркнуть в нем не
только структуру алгоритма, но и взаимосвязь подпрограмм. Взгляни на программы
на рис. 6.5. По ним отыскивается минимум многомерной функции в заданном
диапазоне существования аргументов (строки 3 и 4).
Рис.6.5. BASIC-программа поиска минимума многомерной функции
методом покоординатного спуска:
а) выделение структуры программ рамками диаграммы;
б) выделение стрелками иерархии подпрограмм;
В программе на рис. 6.5,а рамками структурной диаграммы выделен
алгоритм решения задачи: минимум многомерной функции отыскивается методом
покоординатного спуска с использованием подпрограммы определения минимума
одномерной функции методом золотого сечения. Спуск заканчивается (цикл
"до" на строках 6-10), когда новая точка-кандидат в минимумы будет
отстоять от точки предыдущего приближения менее чем на величину заданной
точности Е. Цикл "до" (строки 14-17) реализован и в подпрограмме "Золотое
сечение" (строки 13-18).
Сама анализируемая функция (ее аргументы – элементы массива Х) должна
быть записана с 21-й строки подпрограммой третьего уровня.
В программе на рис. 6.5, б прослеживается, как при покоординатном
спуске вызывается подпрограмма первого уровня – "золотое сечение" (строка 8). В этой подпрограмме
вызывается (строка 15) подпрограмма второго уровня, где после деления отрезка в
золотом соотношении вызывается (см. строку 20) подпрограмма третьего уровня для
расчета значения функции при текущих значениях аргументов.
Помни! Хорошо оформленная программа может иметь несколько
листингов, каждый из которых иллюстрирует те или иные приемы алгоритмизации или
программирования.
Лучший комментарий программы на незнакомом языке – это программа на знакомом
языке.
К такому приему комментирования программ мы уже прибегали не раз – см. рис. 0.8, 0.9, 0.16,
0.27, 2.7 и 5.6.
Вот еще один пример.
Так как самый распространенный язык персональных компьютеров – BASIC, то на нем и стоит
комментировать программы на других языках программирования, как это сделано в
программе на рис. 6.6 поиска минимума многомерной функции методом
покоординатного спуска.
Рис.6.6. Pascal-программа поиска минимума многомерной функции
методом покоординатного спуска с комментариями, написанными на BASIC'e
Исходными данными расчета будут вид анализируемой функции (на
языке Pascal она задается процедурой, а на BASIC'е – подпрограммой), число
аргументов функции, точность расчета (строка 17 BASIC-программы) и область изменения
значений аргументов, где отыскивается минимум (строки 19 и 20). При
покоординатном спуске задача сводится к многократному определению минимума
одномерной функции, что и делается методом золотого сечения (см. рис. 6.3)
одноименной процедурой на языке Pascal и подпрограммой, начинающейся строкой 3,
на BASIC'е.
Алгоритм программы на PASCAL'е выделен уступами от левого края
листинга, а на BASIC'е
– рамками
структурной диаграммы.
В программе на рис. 6.6 использовано выделение комментариев
круглыми скобками со звездочками, допустимое в одной из версий языка. В других
версиях для этого могут использоваться знак процента, например, или фигурные
скобки.
Внимательный читатель может заметить некоторые различия в PASCAL-
и BASIC-программах на рис. 6.5 (строка 24, например). Это связано с тем, что
PASCAL-процедуры могут иметь локальные переменные (см. совет 5А), и с тем, что
на PASCAL'е (как, кстати, и на некоторых версиях BASIC'а) не могут
сосуществовать одноименные переменные разных типов.
Задание читателю. Реши программный ребус, загаданный только что.
Почему нельзя заменить Х на Х1 в строке 24 BASIC-программы на рис. 6.6?
Можно без всякого комментария выразить сущность программы ее
формой.
Вот пример. На рис. 6.7 дана Pascal-функция определения чисел
Фибоначчи (1,1, 2, 3, 5, 8, 13, 21, 34, 55 и т.д.) рекурсивным алгоритмом.
Числа, записанные в скобках,
– это
не только первые десять чисел Фибоначчи, но и количества символов в строках
Pascal-функции с первой по десятую соответственно. Если читателя шокирует такая
форма записи программы, то он может посмотреть на нормальную запись функции
определения чисел Фибоначчи на рис. 0.38.
Рис.6.7. Pascal-функция поиска чисел Фибоначчи: единство формы и содержания
Читатель, наверно, заметил, что в тексте стоит слово
"функция", а в комментарии на рис. 6.7 – слово "процедура". Это намеренная ошибка, подчеркивающая
прямое "родство" процедур и функций.
Непронумерованный совет. Если процедура имеет только один выходной
параметр, то оформляй ее функцией. Это существенно упростит встраивание ее в
алгебраические выражения (см. предпоследнюю строку на рис. 6.7).
Рисунок 6.7
– не
единственный пример в книге, когда авторы пытались сохранить единство формы и
содержания. Взгляни на рис. 6.3, а, в оглавление, в приложение. В книге 8 глав,
128 советов, столько же использованных операторов, 48 литературных ссылок, 64
задачи, решаемые программами. Более того. В первом издании книги оказалось 256
страниц, а это уж от авторов никак не зависло. Такая причуда имеет много
причин, одна из них в том, что читатель увидит цифру 128 в названии книги и
сразу поймет, что речь идет о советах программисту, а не кому-то другому.
Как бог на душу положит.
Ты, читатель, наверное, уже заметил, что авторы не придерживались
какой-то одной системы в нумерации строк BASIC-программ книги. Строки
нумеровались и подряд (см. программу на рис. 0.12), и десятками (см. программу
на рис. 0.13) и совсем без системы (см. программу на рис. 0.35). Среди номеров
строк можно даже увидеть столь редкий нуль (см. программы на рис. 3.19 и 6.8) и
даже дробные номера (см. рис. 1.16).
В первых руководствах по программированию на BASIC'е было записано
правило: "Строки программы нумеруются числами, кратными десяти, а на одной
строке записывается только один оператор". Необходимость в выполнении
этого требования была вызвана тем, что на ранних версиях BASIC'а можно было
поместить новую строку между уже записанными, дав ей номер, не кратный десяти,
а единственным способом редактирования строки был ее повторный набор. Кроме
того, при программной ошибке машина отмечала только номер строки, где она
произошла. Если же на этой строке много операторов, то иногда бывает непросто
выявить виновника сбоя.
Принимая решение о характере нумерации строк BASIC-программы,
выясни, есть ли в трансляторе возможность перенумерации строк операторами
RENUMER, RESEQ. Нет – нумеруй
десятками, есть – руководствуйся
какими-то другими соображениями.
Помещая много операторов на одной строке, авторы принимали во
внимание два момента.
Во-первых, современные BASIC-машины имеют удобные средства
редактирования программ (вставка и удаление символов) и указывают на конкретный
оператор – виновник
сбоя при программной ошибке.
Во-вторых, авторам удалось при ограниченном объеме книги поместить
в ней довольно много программ, уплотняя их.
Рамки раскрашенных структурных диаграмм, как надеются авторы, не
дадут в них запутаться.
В одной из наиболее совершенных версий BASIC'а – в Квик-BASIC'е (см.,
например, рис. 7.4) – проблема
нумерации строк решена окончательно и бесповоротно: номеров строк совсем нет.
Непронумерованный совет. Потихонечку отучайся от использования
номеров строк и для редактирования программ, и для реализации разветвленных
алгоритмов.
Иногда целесообразно, чтобы программа распечатывала ну если не всю
себя, то уж какую-то свою часть.
Эта проблема восходит к крестному отцу кибернетики Норберту Винеру
(1894-1964). Можно ли написать программу, которая после ее запуска напечатала
бы сама себя? Несмотря на довольно широкую известность задача многими считается
неразрешимой.
Здесь мы сделаем небольшой исторический экскурс.
Колумбу история приписывает два великих дела: во-первых, открытие
Америки, а во-вторых, решение задачи о "колумбовом" яйце. Ее суть
была в том, что яйцо требовалось поставить на острый конец на гладкой
поверхности. Многие пытались для этого раскручивать яйца, делать незаметные
подпорки. Колумб же слегка надломил скорлупу и поставил яйцо торчком.
BASIC'у, как и Колумбу, тоже приписывают два великих дела.
Во-первых, BASIC открыл для многих людей прекрасный мир программирования, а
во-вторых, он по-колумбовски решил задачу о самораспечатывании программы. Так, на
языке PL/1 эта задача решается программой с полусотней операторов, на BASIC'е
же (на многих версиях) любая программа может быть распечатана
одним-единственным оператором LIST как в непосредственном, так и в программном
режиме.
Два великих дела и Колумба, и BASIC'а явно не равнозначны. При
этом если колумбово решение задачи о яйце не нашло практического приложения, то
бейсиковское решение задачи о самораспечатывании программы можно использовать.
Пример тому – программа
на рис. 6.8. Она является модернизацией программы на рис. 3.19.
Рис.6.8. BASIC-программа подбора аппроксимирующего уравнения из
16-ти возможных: двойное использование участка программы
Обе программы позволяют статистически обработать результаты
однофакторного эксперимента, но если вторая (см. рис. 3.19) сама выбирает
формулу из 15 возможных, то первая (см. рис. 6.8) предоставляет возможность
выбора человеку. Поэтому строки 1-16 программы на рис. 6.8 выполняются двояко.
Во-первых, по ним рассчитываются значения функций (строка 29) и, во-вторых,
программа сама их распечатывает (строка 19) перед запросом у пользователя
номера вида регрессии.
Яйцо пытались ставить тупым концом вверх, чтобы его съесть.
Колумбово решение здесь не оптимально – яйцо будет валиться набок. Наше решение о распечатке формул тоже
имеет изъян – у формул
будет ненужный "хвостик" RETURN. Для яйца лучше взять подставку, а
регрессионные уравнения лучше вывести на печать оператором PRINT.
Метод распечатки части программы при ее прогонке настолько
соблазнительный, что авторы не удержались и пару раз использовали его, не
дожидаясь этого совета,
– см.
рис. 3.19 (строка 32 с фантазией авторов) и рис. 6.1 (строка 20 с вполне
реальным применением –
для
распечатки уравнений, входящих в анализируемую систему).
Делай так, чтобы было ясно видно, что у тебя записано в блоке
данных.
Сам совет о комментариях в блоках данных (как, впрочем, и в других
"туманных" местах программы) в особых комментариях не нуждается – см. простейшую программу на
рис. 6.9.
Рис.6.9. BASIC-программа расчета объема параллепипеда: коментарии
в блоке данных
Язык BASIC, работающий, как правило, в режиме интерпретатора,
породил особый вид диалога человека с ЭВМ, сообщающего ей новые условия задачи.
Это условия можно менять, редактируя программу – заменяя цифры в блоке данных.
Непронумерованный совет. Если сумматоры в программе требуют своего
обнуления, то и блоки данных требуют восстановления счетчика считываний. Не
забывай перед блоком данных ставить оператор RESTORE. Это убережет тебя от
ошибок при запуске программы не с самого начала.
Если уж у тебя нет принтера, то сделай так, чтобы владельцы
принтеров завидовали тебе хоть в малом.
Если у тебя нет принтера и тебе приходится списывать тексты
готовых программ с экрана дисплея, то можно разным шрифтом выделять структурные
управляющие конструкции так, как это сделано в программе на рис. 6.10
сортировки массива с ненулевыми элементами.
Рис.6.10. Расцвеченная BASIC-программа сортировки одномерного
массива
Ввод нуля на запрос машины означает конец ввода массива (см. совет
12).
Шрифтом выделяются:
альтернативы и множественные ветвления (курсив);
цикл "пока" и цикл "до" (полужирный);
цикл с параметром (разрядка).
Отсутствие принтера дает еще одно преимущество. Дело в том, что
принтер провоцирует на распечатку огромных "простыней" с колонками
цифр, из которых затем обычно выуживаются одно-два числа с результатом. В
период расцвета пакетного режима ЭВМ вышли на второе место в мире (после газет)
по производству макулатуры. Если принтера нет, то пользователь должен будет еще
немного "поколдовать" над программой, чтобы она ему в конце концов
выдала эти два искомых числа, которые не составляет труда списать с экрана
дисплея.
Когда-нибудь появится дотошный статистик, который подсчитает,
сколько деревьев сберег нам один персональный компьютер, хоть и с принтером, но
подключенный к сети.
Используй шрифт для большей выразительности твердых копий.
На рис. 6.11 помещена программа нелинейной аппроксимации
результатов однофакторного эксперимента.
Рис.6.11. BASIC-программа нелинейной аппроксимации одномерной
табличной зависимости
Собственно расчет коэффициентов аппроксимирующего полинома
заканчивается на строке 80. Далее (строки 90-150) формируется итоговая таблица
результатов расчета и (строки 160-190) предоставляется возможность пользователю
повтора расчета.
Задание читателю. Переделай программу на рис. 6.11 так, чтобы
принтер при печати строки таблицы с отклонением точки от аппроксимирующей
кривой более чем на 5 % менял цвет ленты, а не шрифт, как это показано на рис.
6.12.
Рис.6.12. Протокол прогонки программы нелинейной аппроксимации на
рис.6.11 с шрифтовым выделением точек, отклоняющихся от кривой более чем на 5%
Заменяй по возможности вывод результатов расчета в виде таблиц на
графики, диаграммы, гистограммы и прочее.
Составление программ графического отображения информации на экране
дисплея – типичный
пример изобретения велосипеда. Есть прекрасные пакеты деловой графики [7, 41],
которые нужно достать (к сожалению, не купить, а именно достать) и использовать
на ПЭВМ. Но авторы рискнули и поместили в книге две программы графического
отображения информации.
Рис.6.13. BASIC-программа построения гистограммы
Первая программа (рис. 6.13) проста. Она строит на дисплее
гистограмму вида
3.185410.12% *****
6.295820.54% **********
и т.д.
Рис.6.14. BASIC-программа построения эквипотенциальных кривых
поверхности второго порядка
Вторая программа (рис. 6.14) посложней. Она пригодится при анализе
функции двух переменных, когда перед точным поиском максимумов и минимумов (см.
программы на рис. 0.10-0.13, 0.34, 6.5 и 6.6) нужно определить их примерное
местоположение. Программа на рис. 6.14 (ее автор Фам Ван Чиен) рисует на экране
дисплея линии одинакового уровня поверхности, описываемой уравнением на строке
360. Здесь, как понимает читатель, нужно каждый раз записывать анализируемую
функцию. Такой специфический начальный диалог с ЭВМ мы отметили в совете 68.
Графические операторы [33], использованные в программе на рис. 6.14, мы не
описали в приложении, так как цель публикации этой программы такая же, как у
программы на рис. 5.15,
– разжигание
любопытства у читателей.
Не стреляй из пушки по воробьям.
Не расписывай структурной лесенкой программу с простым алгоритмом.
Публикуя короткие программы, ради экономии места (и тем самым ради
сбережения леса – см. совет
69) размещай несколько программных строк в одну строку – сравни программы сортировки
массива на рис. 6.15.
Рис.6.15. BASIC-программы сортировки одномерного числового
массива:
а) компактная запись;
б) структурированная запись;
Помни! Интерес людей к твоей программе определяется в первую
очередь ее содержанием, а потом уж формой.
Отметь! Операторы программы на рис. 6.15, б не отделены друг от
друга никакими символами. Мы их выкинули из программы, надеясь, что появятся
подобные трансляторы без знаков-разделителей, несколько засоряющих листинги
программ. Ведь если человек без труда разбирается в программе без двоеточий, то
машина тем более должна разбираться в подобных программах.
Корми пользователя с ложечки.
В случае, когда выдаваемая ЭВМ информация полностью не умещается
на экране дисплея, научи компьютер выдавать ее порциями – так, например, как это
сделано в программе на рис. 5.11 "Картотека приказов". По запросу
пользователя ЭВМ выводит на дисплей карточки картотеки, отвечающие заданному
признаку: имя исполнителя, дата выполнения и прочее. На экране дисплея может
уместиться только четыре карточки. Поэтому в программе на рис. 5.11 в конце
строки 23 устроен счетчик выведенных на дисплей карточек. И если их на дисплее
набирается четыре штуки, то оператор условного перехода строки 22 фиксирует это
и разрешает вывод новых карточек только по команде человека и после очистки
экрана дисплея.
Есть два способа снятия твердых копий с экрана дисплея.
Первый способ штатный. На клавиатуре некоторых ЭВМ есть
специальная кнопка, нажав на которую, можно перенести картинку с дисплея на бумагу
принтера. Вот почему в программы книги мы, как правило, не вносили операторы
печати результата на принтере.
Второй способ адресован тем "счастливцам" (см. совет
69), у которых нет принтера. Экран дисплея можно накрыть фотобумагой, подержать
ее там рассчитанное или определенное опытным путем время, а затем проявить и
зафиксировать. Вот вам и твердая копия, правда, в зеркальном изображении.
(Сражение с медузой Горгоной)
Задание читателю. Разработай программу перевода информации на
экране дисплея в зеркальную копию для того, чтобы иметь правильные фотокопии
без использования промежуточного негатива.
Выжимай из программы все, что она может дать.
В предыдущих советах книги, как правило, одна программа
генерировала один, ну два совета. Программа, помещенная на рис. 6.16, своего
рода рекордсмен: из нее "выужено" советов в полтора раза больше, чем
строк в ней.
Рис.6.16. BASIC-программа поиска точки оптимального расположения:
24 строки и 32 совета
С некоторыми из 32 советов программы читатель уже знаком, но это
не беда, повторение – мать
учения.
Задание читателю. Подсчитайте, суть скольких нижеприведенных
советов 6F00-6F1F мы уже рассматривали в книге. Чем выше будет цифра, тем лучше
усваивание материала книги.
Программа на рис. 6.16 решает задачу о месте встроенного магазина
в микрорайоне (см. также рис. 3.6) и иллюстрирует следующие 32 микросовета.
6F00. Расшифровывай в программе смысл редких операторов и
операторов, имеющих разное написание в разных диалектах языка (см. строку 1,
начало строки 4 и конец строки 17).
6F01. Оформляй название программы не ремаркой, а оператором
печати. Это позволит идентифицировать программу и при ее распечатке, и при ее
прогонке (строка 2).
6F02. Старайся избегать приемов программирования и операторов,
допустимых не на всех диалектах языка. Так, например, оператор ввода с
клавиатуры с комментарием (строки 3, 7, 8) разбивай на два: оператор печати и
оператор ввода; не используй одноименных массивов и простых переменных (строка
11) и т.д.
6F03. Резервируй в памяти ЭВМ место под массивы с некоторым
запасом (навырост – строка
4). Это даст возможность при необходимости в непосредственном режиме увеличить
число элементов массивов, ввести недостающие параметры и повторить расчет,
запустив программу не с самого начала. Так можно легко исправить ошибку – пропуск некоторых параметров
в начальном диалоге.
6F04. Отводи под вещественные по сути величины целочисленные по
форме переменные. Так, заменяя километры на метры (строка 7), рубли на копейки,
тонны на граммы и т.д., можно избежать десятичных запятых и существенно
сэкономить память ЭВМ.
6F05. Помни, что использование целочисленных переменных экономит
память ЭВМ лишь в том случае, если эти переменные объединены в массивы. Имей в
виду, что большинство BASIC-трансляторов имеют одну арифметику и для
вещественных, и для целочисленных переменных. Простые целочисленные переменные
не ускоряют прогонку программы, а только засоряют память и листинг знаками
процента.
6F06. Давай переменным "говорящие", но не очень длинные
имена: ОРТ – оптимальный,
MIN – минимальный
(строка 5).
6F07. Старайся не пользоваться принципом умолчания или по крайней
мере не злоупотребляй им. Этот принцип по-разному толкуется в разных диалектах
BASIC'а, а в таких ортодоксальных языках, как PASCAL, его почти нет: объявляй
переменные в ремарках (строка 5), указывай шаг параметра цикла (строки 6 и 19),
помечай конец программы (строка 24) и т.д.
6F08. Используй на BASIC'е удачные находки других языков
программирования. Так, в языке Си заголовок цикла с параметром может содержать
операторы обнуления сумматоров (строка 12), операторы присвоения (строка 10).
Это не только делает программу более компактной, но и исключает некоторые
ошибки: повторный вход в цикл без обнулений сумматора, например.
6F09. Проводя чистку цикла (строка 11), помни, что уже давно
появились так называемые оптимизирующие трансляторы, берущие такую черновую
работу на себя, оставляя человеку простор для творчества. Кроме того, не
забывай о ясности программы (см. совет 41).
6F0А. Заменяя для
ускорения счета возведение в целую степень на произведение, помни, что такая
операция может иметь обратный эффект, если переменные – индексные, да к тому же
вставленные в выражения (строка 13).
6F0В. При формировании текстовых сообщений не забывай о русских
падежах и других грамматических формах (конец строки 15).
6F0С. Оформляй ремарками концы структурных блоков (строки 16, 21 и
22) во избежание каких бы то ни было двусмысленностей при анализе программы.
6F0D. При формировании таблиц на дисплее и бумаге используй
специальные зоны, попасть в которые помогают запятые в операторах печати
(строки 18 и 20). Это избавит от необходимости использования табуляторов и
форматов печати.
6F0Е. Если в трансляторе нет слова ELSE (иначе), то смело меняй
полную альтернативу на две неполные (строки 21 и 22). При этом не придется
использовать метку, которую многие считают признаком низкой культуры
программирования.
6F0F. Отмечай ремаркой малозаметные для человека особенности
программы, пренебрежение которыми может сильно исказить результат (строка 20).
6F10. Не забудь указать в программе ее имя и внешний носитель, где
она хранится (строки 23 и 24).
6F11. Пустые места в коротких строках заполняй дополнительными
комментариями (строки 6, 9, 12, 14, 23 и 24).
6F12. При выводе результата не забудь распечатать и исходные
данные (строки 18-23). Это, во-первых, позволит оформить полный протокол
расчета, а во-вторых, призовет к дополнительному контролю исходных данных.
6F13. Печатай на листинге программы комментарии другим цветом. Так
будет проще игнорировать их при анализе программы.
6F14. Не жалей пробелы в листинге программы, отделяя ими операторы
на строке, служебные слова и переменные. Это повысит "читабельность"
программы.
6F15. Размещение нескольких операторов на строке делает программу
компактной. Она сможет полностью уместиться на экране дисплея, что упростит ее
разбор, отладку и модернизацию. Если ты к тому же планируешь программу
опубликовать, то ее компактность повысит шансы появления на страницах журнала,
испытывающих дефицит бумаги.
6F16. Нумеруй строки своей программы, несмотря на то, что наиболее
совершенные версии BASIC'а (Турбо-и Квик-BASIC, например) в номерах строк не
нуждаются. Номера строк пригодятся при описании программы и во время отладки,
когда программа запускается не с самого начала, а переменные, хранящие исходные
данные, не обнуляются. Номера строк также необходимы для того, чтобы можно было
воспользоваться советом 6F03 (расширение исходных данных).
6F17. Меняя число пробелов перед первым оператором строки, можно
выделить структуру программы "паскалевским" манером (строки 7 и 8,
11-16 и 20-22). Если при трансляции экономная машина их выкидывает, то обмануть
ЭВМ в ряде случаев удается, поставив после номера строки знак – разделитель операторов.
6F18. Погаси дисплей перед выводом очередной порции информации
(строки 1 и 18). Это исключит перескакивание строк на дисплее.
6F19. Отпечатывай листинг программы дважды по одному и тому же
месту. Это повысит его качество.
6F1А. В итерационные участки программы вставляй операторы печати
промежуточных результатов. Так можно контролировать ход расчета, да и
длительность его в этом случае как бы становится чисто психологически меньше.
6F1В. Подсчитай число элементов списка перед вводом его и сообщи
полученную цифру машине (строка 3). Это снизит вероятность пропуска какого-то
элемента или его двойного ввода.
6F1С. Пусть машина перед выводом ответа даст звуковой сигнал
(строка 17). В этом случае можно дожидаться конца счета без нервного
поглядывания на дисплей.
6F1D. Не помещай в литерных константах цепочки пробелов, кому-то
будет трудно их подсчитывать при вводе программы в ЭВМ (строки 2 и 3). В таких
местах лучше использовать операторы ТАВ.
6F1Е. Если тебе не нужен нулевой элемент массивов, то дай знать об
этом машине (строка 4).
6F1F. Старайся не использовать в программах латинскую букву I. Ее
многие путают с единицей.
Из перечисленных 32 советов 10 можно назвать относительно новыми,
ранее не освещенными в книге. Ниже дан перечень их. Читателю, которому эта
информация может помешать выполнить вышеприведенное задание, рекомендуем здесь
на время зажмуриться. Остальным сообщаем: 6F00, 6F01, 6F02, 6F0В, 6F0С, 6F0F,
6F10, 6F12, 6F13 и 6F1D.
Автор задачи об оптимальном месте для торгового центра (см.
программы ее решения на рис. 2.2, 2.3, 3.6 и 6.16) – ленинградец А. Лебедев.
Заставка 7
Сюда попали советы, которые никаким боком нельзя было приложить к
тематикам предыдущих глав. Злые языки могут утверждать, что цель этих советов – округление числа глав до
восьми, а советов – до 128.
Но мы не боимся злых языков. Число советов в книге, если считать и
непронумерованные, и те, какие вдумчивый читатель прочтет между строками
программ и текста, уже давно перевалило за цифру, стоящую в заглавии.
Тем не менее, вот тебе, читатель, еще шестнадцать советов.
Копеечным шилом можно удвоить емкость архивной памяти.
Рассказывают историю про то, как одна электротехническая фирма объявила
премию в 1000 марок тому, кто исправит заклинившийся электродвигатель. Один
человек исправил поломку одним ударом молотка, а затем обосновал цену этой
работы так: 1 марку за удар и 999 марок за знание места, куда нужно было
ударить.
Подобную работу с хорошим эффектом можно выполнить, проделав в
конверте восьмидюймового гибкого магнитного диска для позиционирования его в
дисководе еще одно отверстие, симметричное первому. Диск из одностороннего
превратился в двусторонний. Главное, чтобы запись на одной стороне не портила
запись на другой.
Другой пример умного удара молотком. В мире очень популярен
английский компьютер "Синклеар". Он стоит дешевле электронных часов,
но у него есть один серьезный недостаток: зацикливание программы эквивалентно
ее потере, так как прервать работу компьютера можно, только выключив его из
электросети. Так вот, замыкание одного-единственного конденсатора этого
компьютера равносильно нажатию клавиши RESET у его более дорогих собратьев.
Нужно только знать, куда ударить молотком, т.е. к какому конденсатору подпаять
выключатель.
Мы, конечно, не призываем пользователей бить молотком по своему
компьютеру даже в фигуральном смысле, но... см. совет 74.
Если у тебя нет графических операторов – будь ими.
К экрану дисплея можно прикрепить прозрачную пленку со сделанным
цветными фломастерами рисунком. Так можно получить цветной и графический
эрзац-дисплей, накладывая на постоянный рисунок переменную алфавитно-цифровую
информацию. При особой нужде водо- или спиртосмываемыми фломастерами можно
рисовать и по стеклу дисплея.
Такие цветные трафареты можно пронумеровать и прикреплять к экрану
дисплея по команде ЭВМ, осуществляя смену графических кадров.
Принтер, не требующий перфорированной бумаги, можно заправить и
бумажным полотенцем, свернутым в рулон, и даже его более узким аналогом. Нужно
при этом только отрегулировать ширину печати.
Этим советом мы заканчиваем серию из трех советов под девизом
"Голь на выдумки хитра", хотя ее можно продолжать до бесконечности,
учитывая качество, надежность и комплектность отечественной вычислительной
техники.
Переименовывай клавиши при перепрограммировании их функций.
Если предохранительный колпачок на клавише клавиатуры (речь о нем
шла в совете 1В) сделать эластичным и написать на нем функцию клавиши, то
станет более удобным ее перепрограммирование. Для некоторых ЭВМ выпускаются,
кстати, пластиковые эластичные накладки на клавиатуру, повторяющие ее форму.
Такой нехитрый прием позволяет легко переходить, например, на работу с другим
алфавитом или даже с другим языком программирования, когда служебные слова
языка вводятся нажатием всего лишь одной клавиши как, к примеру, на ЭВМ
"Искра 226". В последнее время появились клавиатуры, надписи на
которых формируются жидкими кристаллами, что позволяет легко менять их при
переходе на другой регистр или при работе с графическими символами.
Кроме того, на дисплеях выделяются специальные окна для указания функции
программируемых клавиш. Особое удобство представляют дисплеи с контактными
экранами, работать с которыми можно и без клавиатуры, нажимая на экране нужные
места.
Будь любопытен.
Еще раз сформулируем совет, о котором мы уже вскользь где-то
говорили.
Беря в руки описание конкретной версии языка программирования,
имей в виду, что такие руководства для пользователей, как правило, пишутся не
разработчиками трансляторов, а совсем другими людьми. Авторы описаний языков
программирования могут и не знать всех тонкостей и деталей конкретного
транслятора, так как не имели на руках оригинального руководства для
пользователя, а просто поработали с транслятором.
Будь любопытен. Испытывай на своей машине операторы и конструкции,
не описанные в руководстве, но имеющиеся в других версиях языка.
Конкретный пример.
Мало где написано, что числовые переменные в последних версиях
BASIC'а по совместительству выполняют функции булевых (логических) переменных.
Испытай свою BASIC-машину странной на первый взгляд конструкцией А=В=С
("по-паскалевски" это будет выглядеть так: А:=В=С). Если ЭВМ не
выдаст сообщение о синтаксической ошибке, то переменная А (в этом случае
булевая) примет нулевое значение, если В=С, и не нулевое (обычно минус
единицу), если В=С.
Авторы в книге нередко дают взаимоисключающие советы. Поступить
так или иначе, а может быть совсем по-третьему – решать читателю.
Не используй предыдущий совет в обычной практике программирования.
Побереги его для различных программных курьезов или для воплощения правила о
том, что программа без тайны
– не
программа.
Нужна. И не только педаль, а другие устройства ввода сенсорной
информации помимо клавиатуры.
На клавиатуре ЭВМ есть клавиша, нажатие которой приостанавливает
бег строк на экране дисплея (клавиши пробела, BREAK, CTRL + S и др.).
Если ты, читатель, имеешь склонность модернизировать не только
программы компьютера, но и сам компьютер, то можно посоветовать тебе приделать
к ЭВМ "педаль тормоза". Неплохо, если функция этой ножной клавиши
клавиатуры будет перепрограммируемая: приостановка бега строчек на дисплее,
переход на верхний регистр клавиатуры и др. Ведь как часто нам при работе с компьютером
не хватает не только головы, но и рук. Они при отладке программы заняты
карандашом, черновиками программ, распечатками результатов расчета и т.п.
Еще лучше, если ты, читатель, приделаешь к ЭВМ не педаль тормоза,
а такие полезные вещи, как мышь для ввода графической информации, игровой
пульт, цифровую мини-клавиатуру и др., руководствуясь принципом "Мы не
будем ждать милостей от отечественной электронной промышленности, сделать их – наша задача".
На рис. 3.21 была помещена полупрограмма-полушутка поиска простых
чисел, когда ЭВМ сама включает и выключает свою периферию.
Программа на рис. 3.21 нереальна не только потому, что нет в
трансляторе соответствующих операторов, но и потому, что есть строгие правила
включения периферии "Круги на воде". Сначала включают централь и до
начала работы с ней – всю
нужную периферию. Выключают машину в обратном порядке – от периферии к центру.
Включение периферии в момент работы на машине может приводить к сбоям системы,
к ее "зависанию".
Засидевшись у компьютера, не поленись встать и повесить на
рубильник, его питающий, табличку "Не выключать!", дабы уходящие
домой аккуратные коллеги ненароком не обесточили тебя и не пустили насмарку твой
дневной труд.
Старайся, чтобы программа в ОЗУ и ее копия на внешнем носителе как
можно меньше отличались друг от друга.
Расширяя и отлаживая программу, не забывай периодически сбрасывать
полуфабрикаты на внешней носитель памяти. Они могут пригодиться по крайней мере
в трех случаях:
а) см. предыдущий совет;
б) при зависании машин (они имеют такую дурную привычку); их
приходится в этом случае отключать, включать и снова загружать
"систему" (операционные системы, трансляторы языка);
в) для возвращения к ранним вариантам программы, если в последних
запутался.
Снабжай полуфабрикаты программ подробными комментариями, иначе в
них тоже можно запутаться.
С другой стороны, не забывай периодически чистить рабочий
магнитный диск от устаревших дублей программы.
Наиболее важные программы и файлы данных всегда имей как минимум в
двух экземплярах на двух дисках (магнитных лентах).
Не полагаясь на аккуратность пользователей, системные программисты
разработали средства автоматической перезаписи полуфабрикатов развиваемой и
отлаживаемой программы.
Не делай переходов к строкам с одной ремаркой и не выкидывай
ремарки при вводе в ЭВМ чужой программы.
Не делай условных и безусловных переходов к строкам программы,
содержащим только комментарии (ремарки). Позже ты сам или твой
товарищ-программист будут вводить эту программу в машину с листинга,
"выжимая из нее воду", опуская комментарии и тем самым допуская
ошибку перехода к несуществующему номеру строки. Большинство BASIC-машин
реагируют на такую ситуацию аварийным остановом при выполнении программы, но
есть машины, передающие управление на строку с номером, ближайшим большим
номера отсутствующей строки.
Сравни две программы: на рис. 2.9 и на рис. 2.13. В первой (рис.
2.9 – поиск
оптимальной точки на карте) вызов подпрограмм на строках 220-250, 260-290 и
300-430 ведется ссылками на первую "действующую" строку. Во второй
(рис. 2.13 – испытание
"Вечного календаря") вызов подпрограмм на строках 100-160 и 170-260
ведется ссылками на строки с комментариями. Пропуск строк с комментариями при
вводе программы на рис. 2.13 чреват ошибкой, а при вводе программы на рис. 2.9 – нет.
Есть единственный способ узнать, персональный ли у тебя компьютер
или это терминал большой машины.
На персональном компьютере время выполнения конкретной программы – величина постоянная, а на
компьютере с разделением времени работы процессора оно зависит от загрузки
соседних терминалов (дисплеев).
При замере интервалов времени иногда можно обойтись и без
встроенных часов компьютера.
Далеко не все персональные компьютеры имеют встроенные часы, а необходимость
в них ощущается довольно часто при написании, например, программ для машин,
работающих в реальном режиме времени (см. программу "Электронный
секретарь" на рис. 4.2).
Но встроенные часы компьютера можно смоделировать, запустив цикл
(время выполнения одного такта известно).
Рис.7.1. BASIC-программа проверки скорости реакции человека:
имитация работы таймера
Такой прием работы компьютера в реальном режиме времени реализован
в программе на рис. 7.1, позволяющей определить среднюю скорость реакции
человека за несколько измерений.
Подобную программу мы уже разбирали (см. рис. 5.16), но в ней мы
использовали встроенные часы компьютера.
Как проводится замер скорости реакции по программе на рис. 7.1?
Человек поудобнее усаживается у компьютера и в ответ на запрос
машины "Готовы?" (строка 30) нажимает любую цифровую клавишу. После
этого через некоторый интервал времени (он разный в различных замерах) на
дисплее появится сообщение "Пуск!" (строка 90). Тут человек должен
как можно быстрее (скорость реакции!) нажать любую клавишу. Если же он ее
нажмет раньше появления команды "Пуск!" (не выдержали нервы? попытки
перехитрить машину?), то ЭВМ накажет его секундным штрафом (строка 80).
Оператор GET существенно обогащает возможности диалога человека с
компьютером. Работая по программе с оператором GET, компьютер может делать два
дела одновременно – вести
счет и как бы прислушиваться к человеку, передавая управление программой на
новые участки, как только тот нажмет оговоренную или любую клавишу клавиатуры.
Так и вежливый, внимательный человек, ведя свой монолог, прислушивается к
собеседнику, давая ему возможность при первой необходимости вставить свою фразу
или задать уточняющий вопрос.
Эрзац-часы, встроенные в программу, требуют тарировки. Число 0,008
на строке 110 программы говорит о том, что один такт цикла "до" на
строке 100 выполняется машиной за 0,008 с.
Не пытайся смоделировать встроенные часы на компьютере, работающем
в режиме разделения времени процессора между многими пользователями, – вот и пригодился тебе
предыдущий, казалось бы, ни на что не нужный совет.
Непронумерованный совет. Не отмахивайся от советов книги, даже
если они тебе с первого раза покажутся непрактичными и даже вредными. Вдруг они
на что-нибудь пригодятся! Даже на то, чтобы поискать более оптимальное решение.
Очень много игр, в которые играет человек, начинаются с бросания
жребия: перемешивания в мешке фишек лото, перемешивания на столе костяшек
домино, бросания игральных костей, тасовки карт. Программирование подобных игр
начинается с обучения компьютера тасовке кар, например, для генерации целых
неповторяющихся случайных чисел в заданном диапазоне.
Имея под рукой генератор псевдослучайных чисел и функцию выделения
целой части числа, несложно получить ряд целых случайных чисел. Сложнее
добиться, чтобы эти числа не повторялись.
Программы на рис. 7.2 и 7.3 позволяют получать такие ряды чисел,
но делают это по разным алгоритмам.
Рис.7.2. BASIC-программа выдачи неповторяющихся целых чисел:
использование булевого массива
Рис.7.3. BASIC-программа выдачи неповторяющихся целых чисел:
использование отсортировки двумерного массива
По программе на рис. 7.2 создается массив А, вещественный по форме,
но булевый по содержанию. его элементы хранят информацию о том, выходило ли
число, совпадающее с номером элемента массива, или нет. Если, например, А(5)=0,
то это значит, что числа пять еще не было. Если же А(9) не равно нулю, то
девятка уже выпадала. Поэтому в теле цикла с параметром на строке 2 записан
цикл "до" – генерация
целого случайного числа повторяется до тех пор, пока машина не убедится, что
такого числа еще не было.
Алгоритм генерации целых случайных неповторяющихся чисел от 1 до N
по программе на рис. 7.3 иной. Сначала машина заполняет двумерный массив А
следующим образом. Элементы первого столбца принимают значения номера строки 1,
а элементы второго столбца
– значения
случайных вещественных чисел в интервале от 0 до 1 (строка 7). Затем массив А
сортируется в порядке убывания значений элементов второго столбца (строки
8-11). Это влечет за собой перемешивание значений элементов первого столбца – целых чисел в диапазоне от 1
до N.
В программе на рис. 7.3 реализованы три способа инициализации
генератора случайных чисел:
использование в качестве базы генератора псевдослучайных чисел
показаний встроенного секундомера ЭВМ (строк 1);
холостая прокрутка генератора до тех пор, пока человек не нажмет
какую-либо клавишу клавиатуры (строки 2 и 3);
холостая прокрутка генератора случайное число раз, зависящее от
показания встроенных часов компьютера (строка 4).
Непронумерованный совет.
Оформляй сообщения машины двуязычно (строка 2 в программе на рис.
7.3). Это, во-первых, будет приучать отечественного пользователя к работе с
импортными программными продуктами, а во-вторых, повысит шансы программы
перешагнуть государственные границы.
Если нет уверенности, что резервируемый массив (строка 6 на рис.
7.3) уместится в памяти машины, то "отработанную" часть программы
можно стереть (строка 5).
Программа на рис. 7.2 работает по алгоритму, похожему на
вытаскивание карт из середины колоды. Согласно программе на рис. 7.3 колода
карт сначала тасуется, а затем с нее слистываются отдельные карты.
Программа на рис. 7.2 проще программы на рис. 7.3, но имеет один
существенный недостаток
– у
нее переменная (убывающая) скорость генерации целых случайных неповторяющихся
чисел.
Очень часто, решая ту или иную проблему, мы оказываемся в шкуре
буриданова осла. Задача может иметь два альтернативных решения, как две охапки
сена слева и справа от упомянутого животного. Доводы "за" и
"против" принятия каждого уравновешены. Как в этом случае поступить?
Ехать или не ехать в командировку? Строить или не строить запроектированный
завод? Поистине, гамлетовские вопросы задает нам жизнь!
Некоторые в таких ситуациях бросают монету, другие загадывают
мужчину или женщину и смотрят в окно, ожидая, кто первый появится. Но все это
ненаучные методы. Монетка может куда-нибудь закатиться, а по улице, как назло,
за целый час кошка только и пробежит...
Есть проверенный столетиями метод принятия подобных решений.
Достаточно разложить пасьянс. Сошелся – решение принято, все сомнения прочь. Можно еще подыскать доводы в
его пользу, и оно будет твердо воплощаться в жизнь. Пасьянс психологически нас
на это настраивает.
Но принять решение подобным образом бывает иногда трудно, так как
не всегда под рукой есть колода карт, да и не совсем удобно раскладывать их на
рабочем месте. Но это можно сделать и на экране дисплея ЭВМ.
Программа на рис. 7.4 позволяет разложить старинный пасьянс
"Турецкий платок" по таким правилам.
Рис.7.4. BASIC-программа раскладки пасьянса "Турецкий
платок"
Из одной колоды карт в 52 листа выкладывают картинкой вверх пять
рядов по 10 карт в каждом (рис. 7.5). Последние две карты кладут в шестой
неполный ряд на любое место, как правило, к первому (у нас он будет нулевой) и
второму (первому) столбцам.
Требуется распустить этот турецкий платок, снимая за один ход по
две нижние карты одного достоинства (тройки, дамы и т.д.) из разных столбцов.
Рис.XXIX.
Рис.7.5. Образец раскладки пасьянса "Турецкий платок" на
экране дисплея
На рис. 7.5 показана раскладка пасьянса после снятия двух пар
карт. По правилам игры сейчас можно снять двух королей либо двух дам. Для этого
нужно нажать на клавиатуре две клавиши в любом порядке – 0 и 2 или 1 и 3, что
соответствует номерам столбцов, которые эти карты замыкают. Изображения
снимаемых карт на экране сотрутся, игру можно продолжить – откроются двойки.
Если удастся снять все 52 карты, то машина в награду сыграет
начало "Турецкого рондо" Моцарта. Это означает, что в командировку
все-таки ехать, а завод построить стоит.
Если же при игре станет ясно, что пасьянс разобрать невозможно, то
остановить игру поможет клавиша "ы" (STOP).
В таком случае играющий услышит лишь музыкальную гамму. С
принятием положительных решений по поставленным проблемам пока нужно
повременить.
Последний непронумерованный совет.
Имей в матобеспечении компьютера игры, головоломки, пасьянсы и
прочее и, работая над сложной программой, периодически отвлекайся, чередуя дело
с потехой.
Кстати, раскладка пасьянса, показанная на рис. 7.5, сходится. Как
легко это понять человеку и как трудно написать программу для ЭВМ,
"понимающую" это.
Последнее задание читателю. Составь программу – перевертыш программы на рис.
7.4: человек сообщает машине начальную конфигурацию карт в раскладке пасьянса
"Турецкий платок", а машина отвечает, раскладывается он или нет.
Составление этой программы будет одним из свидетельств того, что
ты, читатель, теряешь статус начинающего.
Суть совета сформулирована устами жреца Лаокоона, заклинавшего
троянцев не втаскивать в город деревянного коня:
"Либо ахейцы за досками этими скрыты,
Либо враги возвели громаду эту, чтобы нашим
Стенам грозить, дома наблюдать и в город проникнуть.
Тевкры, не верьте коню: обман в нем некий таится.
Чем бы он ни был, страшусь и дары приносящих данайцев."
(Вергилий, "Энеида", пер. С. Ошерова).
Большинство программ, иллюстрирующих советы книги, написаны на
BASIC'е. В последнее время критика в адрес этого самого популярного языка
программирования сильно изменилась. Прежде его ругали за неструктурность, за
необязательность и неряшливость в описании переменных, за медленность работы
интерпретаторов и, наконец, за то, что он прививает начинающим плохой стиль
программирования, исправить который потом практически невозможно. Сейчас же
люди, знакомые с последними версиями этого языка (Turbo- и QuickBASIC'ом), если
и ругают его, то за то, что он беззастенчиво "крадет" у других языков
(у языков Pascal, Ada, Modula, C и др.) их полезные качества.
Так, программы, написанные на Quick-BASICе, мало чем отличаются от
Pascal-программ. Но, приближаясь к Pascal'ю, QuickBASIC оставил такое
"родимое пятно", как необязательность описания типов переменных: в
BASIC-программу можно "нырять" сразу, переложив на плечи машины заботу
об определении статусов переменных и об их начальных нулевых значениях.
Приверженцы языка BASIC считают такую особенность языка
достоинством, а приверженцы языка Pascal – недостатком. Но скорее всего правы и те, и другие, если вспомнить,
что наши недостатки – это очень
часто продолжение наших достоинств. При написании коротких программ (а они
преобладают в инженерной практике) описание переменных часто бывает лишним и
только раздражает программистов-дилетантов, составляющих основной круг
пользователей BASIC'а. Но отсутствие описания переменных чревато грубыми
ошибками. На рис. 7.6 помещена программа "Вечный календарь" – аналог программы на рис.
0.2, но переписанной на языке QuickBASIC.
Рис.7.6. BASIC- программа "Вечный календарь"
Мало кто заметит ошибку в конце строки 7, где в имени переменной,
хранящей номер дня, буква a заменена на букву e, что, кстати, более
соответствует английской транскрипции слова "день": дей, а не дай.
Эту ошибку пропустит и интерпретатор языка QBasic, сочтя по принципу умолчания
Dey за новую переменную и присвоив ей нулевое значение. Pascal же такую ошибку
не пропустит, выдав сообщение
– "ссылка
на переменную, не объявленную в заголовке программы". Только в во второй
версии языка Visual Basic для графической оболочки Windows появился оператор Option Explicit,
ввод которой в программу запрещает использовать в ней необлявленные переменные.
Традиционные
версии языка BASIC, руководствуясь принципом умолчания, могут оказать
программисту и другие "медвежьи услуги". Так, например, при работе по
программе "Нелинейная аппроксимация" (см. рис. 0.14) может возникнуть
ситуация возведения нуля в нулевую степень, когда один из аргументов исходной
табличной зависимости равен нулю. В такой ситуации интерпретатор языка BASIC PC
Wang-2200 выдаст сообщение о некорректной операции, интерпретатор же языка
QBasic по принципу умолчания решит, что неопределенность "ноль в степени
ноль" в степени 0 стремится к единице. В программе на рис. 0.14 это так и
есть на самом деле, но в других ситуациях такая услужливость чревата ошибками.